题目大意
有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
*/