参考资料:胡伯涛《最小割模型在信息学竞赛中的应用》
1.什么是最大权闭合子图?
如图所示(图是网上找的)
如果对于一个点集合,其中任何一个点都不能到达此集合以外的点,这就叫做闭合子图。每个点都有一个权值,那么最大权闭合子图就是权值最大的那个闭合子图。
图中的闭合子图有空集,{5},{2,5},{4,5},{2,4,5},{3,4,5},{1,2,4,5},{1,2,3,4,5}
其中最大权闭合子图是{3,4,5}
2.怎么求最大权闭合子图?
首先构造一张网络流图,把每个正权值的点i连接(s,i,x)(x是权值)。把每个负权值的点i连接(i,t,-x),原图中的边连接(i,j,正无穷)然后求最小割,所有正权值之和减去最小割就得到了最大权闭合子图。
3.为什么?(以下证明较为晦涩难懂)
我这个蒟蒻看同一段话看了两个小时才勉强明白。
定义简单割:就是割边都与源点或汇点连接的割。
证明1:最小割一定是简单割
这个证明很简单吧......因为如果是一条不和源点与汇点连通的点的话,那么这条边的容量就是正无穷,那么就不可能是最小割
证明2:简单割和闭合子图存在一一对应关系(表示的东西:一个简单割表示不在闭合子图的正权点和在此闭合子图里的负权点)
这个证明有点绕......详细的还是看pdf吧....
证明闭合图是简单割:如果闭合图不是简单割,那么有一条容量为正无穷的边,说明闭合图中有一条出边不在闭合图中,不符合闭合图的定义
证明简单割是闭合图:简单割里没有容量为正无穷的边,所以假如S(起点那一块)中的点s可以到达一点t,那么如果t是正权,则在S中,如果t是负权,则不在T中(终点那一块)
证明3:取最小割的时候得到的是最大权
那么w(闭合图)=闭合图中正权-(-闭合图中负权)
回忆一下简单割的表示的东西,可以得到:
w(闭合图)+割=闭合图中正权-(-闭合图中负权)+非闭合图正权+(-闭合图中正权)
那么w(闭合图)+割=闭合图中负权+非闭合图中正权
w(闭合图)=正权点之和-割
所以要让闭合图最大,就要让割最小
4.应用
hoj2634就是这样的题
#include<iostream>
#include<cstdio>
#include<cstring>
#include<climits>
#include<iomanip>
#include<vector>
#include<cmath>
#include<algorithm>
#include<map>
using namespace std;
int m,n,T,sum,s,t;
long long flow[20005];
int go[200005],h[205],ne[200005],level[205],que[205];
long long inf=1999999999,he;
void add(int x,int y,long long z){
sum++;go[sum]=y;ne[sum]=h[x];h[x]=sum;flow[sum]=z;
sum++;go[sum]=x;ne[sum]=h[y];h[y]=sum;flow[sum]=0;
}
bool bfs(){
int i,head=1,tail=1,from;
for(i=s;i<=t;i++)level[i]=0;
level[s]=1;que[1]=s;
while(head<=tail){
from=que[head];
if(from==t)return 1;
for(i=h[from];i!=-1;i=ne[i])
if(level[go[i]]==0&&flow[i]>0){
level[go[i]]=level[from]+1;
tail++;que[tail]=go[i];
}
head++;
}
return 0;
}
long long dfs(int x,long long liu){
int i;
long long sum=0,kl;
if(x==t)return liu;
for(i=h[x];i!=-1;i=ne[i])
if(level[go[i]]==level[x]+1&&flow[i]>0){
kl=dfs(go[i],min(liu-sum,flow[i]));
sum+=kl;flow[i]-=kl;flow[i^1]+=kl;
if(sum==liu)return sum;
}
return sum;
}
long long find(){
long long ans=0;
while(bfs())ans+=dfs(s,inf);
return ans;
}
int main()
{
int i,j,num,y;
long long x;
scanf("%d",&T);
while(T){
T--;sum=1;he=0;
scanf("%d%d",&n,&m);
s=0;t=n+m+1;
for(i=s;i<=t;i++)h[i]=-1;
for(i=1;i<=n;i++){scanf("%lld",&x);add(s,i,x);he+=x;}
for(i=1;i<=m;i++){scanf("%lld",&x);add(i+n,t,x);}
for(i=1;i<=n;i++){
scanf("%d",&num);
for(j=1;j<=num;j++){scanf("%d",&y);add(i,y+n+1,inf);}
}
printf("%lld\n",he-find());
}
return 0;
}
/*项目:1~n,人:n~m+n*/