题目链接: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 ", pos - dinic(S,T,N)); 107 } 108 }