[HIHO119]网络流五·最大权闭合子图(最大流)

题目链接:http://hihocoder.com/contest/hiho119/problem/1

题意:中文题意。

由于1≤N≤200,1≤M≤200。最极端情况就是中间所有边都是满的,一共有N*M条边,则最多有40000条边。

对于这样的问题有统一的建图策略,提取出问题的最终形态即可按照如下方式建图:

首先建立源点s和汇点t,将源点s与所有权值为正的点相连,容量为权值;将所有权值为负的点与汇点t相连,容量为权值的绝对值;权值为0的点不做处理;同时将原来的边容量设置为无穷大。

利用结论:最大权闭合子图的权值等于所有正权点之和减去最小割。

证明:

1. 最小割一定是简单割

简单割指得是:割(S,T)中每一条割边都与s或者t关联,这样的割叫做简单割。

因为在图中将所有与s相连的点放入割集就可以得到一个割,且这个割不为正无穷。而最小割一定小于等于这个割,所以最小割一定不包含无穷大的边。因此最小割一定一个简单割。

2. 简单割一定和一个闭合子图对应

闭合子图V和源点s构成S集,其余点和汇点t构成T集。

首先证明闭合子图是简单割:若闭合子图对应的割(S,T)不是简单割,则存在一条边(u,v),u∈S,v∈T,且c(u,v)=∞。说明u的后续节点v不在S中,产生矛盾。

接着证明简单割是闭合子图:对于V中任意一个点u,u∈S。u的任意一条出边c(u,v)=∞,不会在简单割的割边集中,因此v不属于T,v∈S。所以V的所有点均在S中,因此S-s是闭合子图。

 

由上面两个引理可以知道,最小割也对应了一个闭合子图,接下来证明最小割就是最大权的闭合子图。

首先有割的容量C(S,T)=T中所有正权点的权值之和+S中所有负权点的权值绝对值之和。

闭合子图的权值W=S中所有正权点的权值之和-S中所有负权点的权值绝对值之和。

则有C(S,T)+W=T中所有正权点的权值之和+S中所有正权点的权值之和=所有正权点的权值之和。

所以W=所有正权点的权值之和-C(S,T)

由于所有正权点的权值之和是一个定值,那么割的容量越小,W也就越大。因此当C(S,T)取最小割时,W也就达到了最大权。

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 
  4 typedef struct Edge {
  5     int u, v, w, next;
  6 }Edge;
  7 
  8 const int inf = 0x7f7f7f7f;
  9 const int maxn = 500;
 10 
 11 int cnt, dhead[maxn];
 12 int cur[maxn], dd[maxn];
 13 Edge dedge[maxn*maxn];
 14 int S, T, N;
 15 int n, m;
 16 
 17 
 18 void init() {
 19     memset(dhead, -1, sizeof(dhead));
 20     for(int i = 0; i < maxn; i++) dedge[i].next = -1;
 21     cnt = 0;
 22 }
 23 
 24 void adde(int u, int v, int w, int c1) {
 25     dedge[cnt].u = u; dedge[cnt].v = v; dedge[cnt].w = w; 
 26     dedge[cnt].next = dhead[u]; dhead[u] = cnt++;
 27     dedge[cnt].u = v; dedge[cnt].v = u; dedge[cnt].w = c1; 
 28     dedge[cnt].next = dhead[v]; dhead[v] = cnt++;
 29 }
 30 
 31 bool bfs(int s, int t, int n) {
 32     queue<int> q;
 33     for(int i = 0; i < n; i++) dd[i] = inf;
 34     dd[s] = 0;
 35     q.push(s);
 36     while(!q.empty()) {
 37         int u = q.front(); q.pop();
 38         for(int i = dhead[u]; ~i; i = dedge[i].next) {
 39             if(dd[dedge[i].v] > dd[u] + 1 && dedge[i].w > 0) {
 40                 dd[dedge[i].v] = dd[u] + 1;
 41                 if(dedge[i].v == t) return 1;
 42                 q.push(dedge[i].v);
 43             }
 44         }
 45     }
 46     return 0;
 47 }
 48 
 49 int dinic(int s, int t, int n) {
 50     int st[maxn], top;
 51     int u;
 52     int flow = 0;
 53     while(bfs(s, t, n)) {
 54         for(int i = 0; i < n; i++) cur[i] = dhead[i];
 55         u = s; top = 0;
 56         while(cur[s] != -1) {
 57             if(u == t) {
 58                 int tp = inf;
 59                 for(int i = top - 1; i >= 0; i--) {
 60                     tp = min(tp, dedge[st[i]].w);
 61                 }
 62                 flow += tp;
 63                 for(int i = top - 1; i >= 0; i--) {
 64                     dedge[st[i]].w -= tp;
 65                     dedge[st[i] ^ 1].w += tp;
 66                     if(dedge[st[i]].w == 0) top = i;
 67                 }
 68                 u = dedge[st[top]].u;
 69             }
 70             else if(cur[u] != -1 && dedge[cur[u]].w > 0 && dd[u] + 1 == dd[dedge[cur[u]].v]) {
 71                 st[top++] = cur[u];
 72                 u = dedge[cur[u]].v;
 73             }
 74             else {
 75                 while(u != s && cur[u] == -1) {
 76                     u = dedge[st[--top]].u;
 77                 }
 78                 cur[u] = dedge[cur[u]].next;
 79             }
 80         }
 81     }
 82     return flow;
 83 }
 84 
 85 int main() {
 86     // freopen("in", "r", stdin);
 87     int k, u, v, w;
 88     while(~scanf("%d %d", &n, &m)) {
 89         init(); S = 0; T = n + m + 1; N = T + 1;
 90         int pos = 0;
 91         for(int i = 1; i <= m; i++) {
 92             scanf("%d", &w);
 93             if(w != 0) adde(n+i, T, w, 0);
 94             else adde(n+i, T, inf, 0);
 95         }
 96         for(int i = 1; i <= n; i++) {
 97             scanf("%d %d", &w, &k);
 98             pos += w;
 99             if(w != 0) adde(S, i, w, 0);
100             else adde(S, i, inf, 0);
101             for(int j = 0; j < k; j++) {
102                 scanf("%d", &v);
103                 adde(i, n+v, inf, 0);
104             }
105         }
106         printf("%d\n", pos - dinic(S,T,N));
107     }
108 }

 

posted @ 2016-10-12 14:23  Kirai  阅读(502)  评论(0编辑  收藏  举报