[洛谷P1361 小M的作物] 最小割建模
阿新 • • 發佈:2018-12-10
題目大意
有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 */