[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 }

 



posted @ 2017-07-28 19:35  skylee03  阅读(144)  评论(0编辑  收藏  举报