一个有向图的闭合图是指:该有向图的一个点集,且该点集的所有出边都指向该点集。最大权闭合图即是其中点权和最大的闭合图。
考虑所有正权的点与源点相连容量为点权,所有负权的点与汇点相连容量为点权相反数的边,图中原边建立容量为正无穷的边。
定义简单割:割边只存在与源点或与汇点相连的边。证明上面建图最小割为简单割:所有与源或汇关联的边组成的割集容量是有限的,为所有点权的绝对值和,最小割容量也至多为该绝对值和。故最小割不可能取任何容量为正无穷的边,即最小割为简单割。
证明网络的简单割与原图闭合图一一对应:
1.闭合图对应简单割。假设闭合图对应的不是简单割,那么一定存在一条正无穷的边连接两个点集与闭合图前提矛盾。所有原假设成立。
2.简单割对应闭合图。由于是简单割,所以最小割与源点相连的点集的出边一定在自己原来所在点集内。所以命题成立。
综上所述,新建网络的简单割与原图闭合图一一对应。
设v1为闭合图的点集,v2为其他点集。v1加源点s得到集合S,v2加汇点t得到集合T.
设点集v中所有正权点形成的集合为v+,所有负权点形成的集合为v-
那么c[S,T]=c[s,v2]+c[s,t]+c[v1,v2]+c[v2,t]=(v2+)+(-(v2-))
设原图闭合图点权之和为Sum=(v1+)+(v1-);
sum+c[S,T]=(v1+)+(v2+);
得sum=(v1+)+(v2+)-c[S,T];即最大闭合图等于原图所有正权点之和-网络最小割。
最大权闭合图建模往往伴随有“必须”等限制。
下面给出一道例题:
P4174 [NOI2006]最大获利
题解:所有基站与汇点建边;对于每个项目:与源点建一条容量为收益的边,每个项目向必须要的基站建容量为正无穷的边表示选项目必须选这两个点。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=55100,M=(1e5+N)*2,inf=1e9; 4 int h[N],e[M],ne[M],f[M],idx; 5 int d[N],cur[N]; 6 int n,m,S,T; 7 void add(int a,int b,int c) 8 { 9 ne[idx]=h[a],e[idx]=b,f[idx]=c,h[a]=idx++; 10 ne[idx]=h[b],e[idx]=a,f[idx]=0,h[b]=idx++; 11 } 12 bool bfs() 13 { 14 memset(d,-1,sizeof d); 15 queue<int>q; 16 q.push(S); 17 d[S]=0; 18 cur[S]=h[S]; 19 while(q.size()) 20 { 21 int t=q.front(); 22 q.pop(); 23 for(int i=h[t];i!=-1;i=ne[i]) 24 { 25 int j=e[i]; 26 if(d[j]==-1&&f[i]) 27 { 28 d[j]=d[t]+1; 29 cur[j]=h[j]; 30 if(j==T)return true; 31 q.push(j); 32 } 33 } 34 } 35 return false; 36 } 37 int find(int u,int limit) 38 { 39 if(u==T)return limit; 40 int flow=0; 41 for(int i=cur[u];i!=-1&&flow<limit;i=ne[i]) 42 { 43 int j=e[i]; 44 cur[u]=i; 45 if(d[j]==d[u]+1&&f[i]) 46 { 47 int t=find(j,min(f[i],limit-flow)); 48 if(!t)d[j]=-1; 49 f[i]-=t,f[i^1]+=t,flow+=t; 50 } 51 } 52 return flow; 53 } 54 int dinic() 55 { 56 int r=0,flow; 57 while(bfs())while(flow=find(S,inf))r+=flow; 58 return r; 59 } 60 int main() 61 { 62 S=0,T=N-1; 63 memset(h,-1,sizeof h); 64 int tot=0; 65 cin>>n>>m; 66 for(int i=1;i<=n;i++) 67 { 68 int x; 69 scanf("%d",&x); 70 add(i,T,x); 71 } 72 for(int i=1;i<=m;i++) 73 { 74 int a,b,c; 75 scanf("%d%d%d",&a,&b,&c); 76 add(n+i,a,inf); 77 add(n+i,b,inf); 78 tot+=c; 79 add(S,i+n,c); 80 } 81 cout<<tot-dinic()<<endl; 82 }