[洛谷P1361 小M的作物] 最小割建模

题目大意

    有n株植物,A,B两块田地,每株植物i,种在A田,可以获得A[i]的收益,种在B田,可以获得B[i]的收益。此外还存在m种额外收益,其中第i中额外收益可以这样描述:如果集合U[i]中的所有植物全部种在A田,那么可以获得exA[i]的额外收益,如果集合U[i]中的所有植物全部种在B田,那么可以获得exB[i]的额外收益,如果并没有全部种在同一块田里,没有任何额外收益。

    请你合理安排种植方案,求出最大收益。

解题思路

     这是经典的最小割模型。我们将源点S当成A田,汇点T当成B田,每株植物抽象成一个点(编号1~n),分别连有向边(S,i,A[i])、(i,T,B[i]),这样,由于最小割中要使得S不能到达T,所以这两边至少要割去一边,我们就实现了第一种代价的取舍。

可是第二步呢?我们考虑构建额外点。对于第i种额外收益,我们建两个额外点A_i,B_i,添加有向边(S,A_i,exA[i])、(B_i,T,exB[i]),然后对于U[i]中的每一个点x,连这样的有向边:(A_i,x,∞)、(x,B_i,∞),这样,如果所有作物都在一块田地,相应的收益边就得以保留,否则必须割去,这样就体现了第二种收益。

    对这样一个图跑一边最大流,也就得到了最小割,也就得到了最少需要舍弃的收益,直接用收益总和sum减去最小割mincut即可得到最大收益。

    本题边数达到百万级别,推荐SAP大法!!(所以说网络流到底可以跑多快啊,完全看不出极限啊……)

P.S.一开始建图的时候标号搞错了,调了N久,还以为sap写萎了……看来网络流题如果发现答案不对,最好优先检查建图!

#include <cstdio>
#include <algorithm>
#define rep(i,j,k) for (i=j;i<=k;i++)
using namespace std;
const int N=5e3+5,M=4e6+5000,INF=2e9;
int En,fst[N],nxt[M],to[M],cap[M];
int n,m,i,x,k,a,b,tot,ans,s[N];
int maxflow,flow,found,h[N],hn[N];
void create(int u,int v,int c) {
	En++; nxt[En]=fst[u]; fst[u]=En; to[En]=v; cap[En]=c;
}
void add(int u,int v,int c) {
	create(u,v,c); create(v,u,0);
}
void sap(int x)
{
	int tmp=flow,Min=tot+1,j,v;
	if (x==n+1)
	{
		found=1;
		maxflow+=flow;
		return ;
	}
	for (j=fst[x];j;j=nxt[j])
	if (cap[j]>0)
	{
		v=to[j];
		if (h[v]+1==h[x])
		{
			flow=min(flow,cap[j]);
			sap(v);
			if (found) break;
			if (h[0]>tot) return ;
			flow=tmp;
		}
		Min=min(Min,h[v]);
	}
	if (found)
	{
		cap[j]-=flow;
		cap[j^1]+=flow;
	}
	else {
		if (Min+1==h[x]) return ;
		hn[h[x]]--; if (hn[h[x]]<1) h[0]=tot+1;
		h[x]=Min+1;
		hn[h[x]]++;
	}
}
int main()
{
	scanf("%d",&n); En=1; tot=n+1;
	rep(i,1,n) scanf("%d",&x),ans+=x,add(0,i,x);
	rep(i,1,n) scanf("%d",&x),ans+=x,add(i,n+1,x);
	scanf("%d",&m);
	while (m--)
	{
		scanf("%d%d%d",&k,&a,&b); ans+=a+b;
		rep(i,1,k) scanf("%d",&s[i]);
		tot++; add(0,tot,a);
		rep(i,1,k) add(tot,s[i],INF);
		tot++; add(tot,n+1,b);
		rep(i,1,k) add(s[i],tot,INF);
	}
	
	hn[0]=tot+1;
	while (h[0]<=tot)
	{
		flow=INF;
		found=0;
		sap(0);
	}
	ans-=maxflow;
	printf("%d\n",ans);
	return 0;
}
/*
	0~tot
*/

猜你喜欢

转载自blog.csdn.net/Orzage/article/details/83783857