Luogu P1251 餐巾计划问题 (最小费用最大流、拆点)

P1251 餐巾计划问题

题目大意:

见题面

思路:

将一天拆点为晚上和白天。

跑最小费用最大流。

在此放两个模板。

Code: 79ms / 3.23MB / 3.03KB C++17 O2
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL N = 8005;
const LL INF = 0x3f3f3f3f3f3f3f3f;
struct MCMF
{
    struct EDGE
    {
        LL nxt, to, cost, flow;
    } ee[N*N];
    LL n, s, t;
    LL cur[N], head[N], vis[N], a[N];
    LL dis[N];
    LL mincost = 0, maxflow = 0;
    inline void AD(LL from, LL to, LL flow, LL cost)
    {
        ee[++cnt].nxt = head[from];
        ee[cnt].to = to;
        ee[cnt].cost = cost;
        ee[cnt].flow = flow;
        head[from] = cnt;
    }
    LL cnt = 1;
    inline void add_edge(LL u, LL v, LL flow, LL cost)
    {
        AD(u, v, flow, cost);
        AD(v, u, 0, -cost);
    }
    inline LL spfa()
    {
        for (LL i = 1; i <= n; ++i)
            dis[i] = INF;
        queue<LL> q;
        q.push(s);
        dis[s] = 0;
        vis[s] = 1;
        while (!q.empty())
        {
            LL u = q.front();
            vis[u] = 0;
            q.pop();
            for (LL i = head[u]; i; i = ee[i].nxt)
            {
                LL v = ee[i].to;
                if (!ee[i].flow)
                    continue;
                if (ee[i].flow && dis[v] > dis[u] + ee[i].cost)
                {
                    dis[v] = dis[u] + ee[i].cost;
                    if (!vis[v])
                    {
                        vis[v] = 1;
                        q.push(v);
                    }
                }
            }
        }
        return dis[t] != INF;
    }
    inline LL dfs(LL u, LL flow)
    {
        if (u == t)
            return flow;
        LL rest = flow;
        vis[u] = 1;
        for (LL i = cur[u]; i && rest; i = ee[i].nxt)
        {
            cur[u] = i;
            LL v = ee[i].to;
            LL w = ee[i].cost, flow = ee[i].flow;
            if (ee[i].flow && (dis[v] == dis[u] + w) && !vis[v])
            {
                LL k = dfs(v, min(flow, rest));
                if (k)
                {
                    maxflow += w;
                    mincost += k * w;
                    ee[i].flow -= k;
                    ee[i ^ 1].flow += k;
                    rest -= k;
                }
            }
        }
        vis[u] = 0;
        return flow - rest;
    }
    void gkd()
    {
        while (spfa())
        {
            for (LL i = 1; i <= n; ++i)
                cur[i] = head[i];
            dfs(s, INF);
        }
    }
    void init(LL nn, LL ss, LL tt)
    {
        n = nn, s = ss, t = tt;
        for (LL i = 0; i <= n; i++)
            head[i] = 0;
    }
} mc;

int main()
{
	LL n, m, s, t;
	cin >> n;
	s = 2 * n + 1; //源点不要0-index(从0开始
	t = 2 * n + 2;
        mc.init(2 * n + 2, s, t);
	for (LL i = 1; i <= n; i++) {
		LL x; cin >> x;
		mc.add_edge(s, i, x, 0);
		mc.add_edge(i + n, t, x, 0);
	}
	LL p, t1, c1, t2, c2; cin >> p >> t1 >> c1 >> t2 >> c2;
	for (LL i = 1; i <= n; i++) {
		if (i + 1 <= n) mc.add_edge(i, i + 1, INF, 0);
		if (i + t1 <= n) mc.add_edge(i, i + n + t1, INF, c1);
		if (i + t2 <= n) mc.add_edge(i, i + n + t2, INF, c2);
		mc.add_edge(s, i + n, INF, p);
	}
        mc.gkd();
        cout << mc.mincost << endl;
	return 0;
}
Code: 1.47s / 2.97MB / 2.05KB C++17 O2
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL N = 8010;
struct node
{
	LL v, flow, cost, next;
}edge[N*N];
LL head[N], dis[N], vis[N], pre[N], rec[N], cnt;
queue<LL> q;
inline void init()
{
	cnt = 0;
	memset(head, -1, sizeof(head));
}
inline void add_edge(LL u, LL v, LL f, LL c)
{
	edge[cnt].v = v; edge[cnt].flow = f; edge[cnt].cost = c;
	edge[cnt].next = head[u]; head[u] = cnt++;
	edge[cnt].v = u; edge[cnt].flow = 0; edge[cnt].cost = -c;
	edge[cnt].next = head[v]; head[v] = cnt++;
}
LL SPFA(LL s, LL t)
{
	memset(dis, 0x3f, sizeof dis);
	memset(vis, 0, sizeof vis);
	while(!q.empty()) q.pop();
	q.push(s); dis[s] = 0; vis[s] = 1;
	while(!q.empty())
	{
		LL tp = q.front(); q.pop();
		vis[tp] = 0;
		LL k = head[tp];
		while(k != -1)
		{
			if(dis[edge[k].v] > dis[tp] + edge[k].cost && edge[k].flow)
			{
				dis[edge[k].v] = dis[tp] + edge[k].cost;
				pre[edge[k].v] = tp; rec[edge[k].v] = k;
				if(vis[edge[k].v] == 0)
				{
					vis[edge[k].v] = 1;
					q.push(edge[k].v);
				}
			}
			k = edge[k].next;
		}
	}
	if(dis[t] == INF) return 0;
	return 1;
}
pair<LL, LL> Mcmf(LL s, LL t)
{
	LL minflow, k, mincost = 0, maxflow = 0;
	while(SPFA(s, t))
	{
		k = t; minflow = INF;
		while(k != s)
		{
			minflow = min(minflow, edge[rec[k]].flow);
			k = pre[k];
		}
		k = t; maxflow += minflow;
		while(k != s)
		{
			mincost += minflow*edge[rec[k]].cost;
			edge[rec[k]].flow -= minflow;
			edge[rec[k]^1].flow += minflow;
			k = pre[k];
		}
	}
	return make_pair(maxflow, mincost);
}

int main()
{
	init(); // do not forget it
	LL n, m, s, t;
	cin >> n;
	s = 0;
	t = 2 * n + 1;
	for (LL i = 1; i <= n; i++) {
		LL x; cin >> x;
		add_edge(s, i, x, 0);
		add_edge(i + n, t, x, 0);
	}
	LL p, t1, c1, t2, c2; cin >> p >> t1 >> c1 >> t2 >> c2;
	for (LL i = 1; i <= n; i++) {
		if (i + 1 <= n) add_edge(i, i + 1, INF, 0);
		if (i + t1 <= n) add_edge(i, i + n + t1, INF, c1);
		if (i + t2 <= n) add_edge(i, i + n + t2, INF, c2);
		add_edge(s, i + n, INF, p);
	}
	pair<LL, LL> ans = Mcmf(s, t);
	cout << ans.second << endl;
	return 0;
}
posted @ 2020-11-10 15:16  Nepenthe8  阅读(70)  评论(0编辑  收藏  举报