[HihoCoder1398]网络流五·最大权闭合子图
题目大意:
有$N$项活动$M$个人,每个活动$act_i$有一个正的权值$a_i$,每个人$stu_i$有一个负的权值$b_i$。
每项活动能够被完成当且仅当该项活动所需的所有人到场。
如何选择活动使最终权值总和最大?
即对于给定的有向无环图,求出最大权闭合子图的权值。
结论:
最大权闭合子图的权值等于所有正权点之和减去最小割。
思路:
引理:
1.最小割一定是简单割;
2.简单割一定和一个闭合子图对应。
即最小割一定对应一个闭合子图,且就是最大权闭合子图。
证明(摘自HihoCoder):
首先有割的容量C(S,T)=T中所有正权点的权值之和+S中所有负权点的权值绝对值之和。
闭合子图的权值W=S中所有正权点的权值之和-S中所有负权点的权值绝对值之和。
则有C(S,T)+W=T中所有正权点的权值之和+S中所有正权点的权值之和=所有正权点的权值之和。
所以W=所有正权点的权值之和-C(S,T)。
1 #include<queue> 2 #include<cstdio> 3 #include<cctype> 4 #include<vector> 5 #include<cstring> 6 inline int getint() { 7 char ch; 8 while(!isdigit(ch=getchar())); 9 int x=ch^'0'; 10 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); 11 return x; 12 } 13 const int inf=0x7fffffff; 14 int s,t; 15 const int E=80800,V=402; 16 struct Edge { 17 int from,to,remain; 18 }; 19 Edge e[E]; 20 std::vector<int> g[V]; 21 int sz=0; 22 inline void add_edge(const int u,const int v,const int w) { 23 e[sz]=(Edge){u,v,w}; 24 g[u].push_back(sz); 25 sz++; 26 } 27 int p[V],a[V]; 28 inline int Augment() { 29 memset(a,0,sizeof a); 30 a[s]=inf; 31 std::queue<int> q; 32 q.push(s); 33 while(!q.empty()&&!a[t]) { 34 int x=q.front(); 35 q.pop(); 36 for(unsigned i=0;i<g[x].size();i++) { 37 Edge &y=e[g[x][i]]; 38 if(!a[y.to]&&y.remain) { 39 p[y.to]=g[x][i]; 40 a[y.to]=std::min(a[x],y.remain); 41 q.push(y.to); 42 } 43 } 44 } 45 return a[t]; 46 } 47 inline int EdmondsKarp() { 48 int maxflow=0; 49 while(int flow=Augment()) { 50 for(int i=t;i!=s;i=e[p[i]].from) { 51 e[p[i]].remain-=flow; 52 e[p[i]^1].remain+=flow; 53 } 54 maxflow+=flow; 55 } 56 return maxflow; 57 } 58 int main() { 59 int n=getint(),m=getint(); 60 s=0,t=n+m+1; 61 for(int i=1;i<=m;i++) { 62 add_edge(n+i,t,getint()); 63 add_edge(t,n+i,0); 64 } 65 int sum=0; 66 for(int i=1;i<=n;i++) { 67 int a=getint(); 68 sum+=a; 69 add_edge(s,i,a); 70 add_edge(i,s,0); 71 for(int k=getint();k;k--) { 72 int v=getint(); 73 add_edge(i,n+v,inf); 74 add_edge(n+v,i,0); 75 } 76 } 77 printf("%d\n",sum-EdmondsKarp()); 78 return 0; 79 }