1. 程式人生 > >[洛谷P1361 小M的作物] 最小割建模

[洛谷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
*/