前置芝士:最大流最小割
一. 啥是闭合图
一件事件x发生需要一个条件:
1. y事件已经发生
2. z事件已经发生
对于这样的依赖关系 , 我们可以用闭合来来描述或者解决
有向图的闭合图(closure)(来源于 胡伯涛《最小割模型在信息学竞赛中的应用》 论文 )
物理意义:事物间依赖关系:一个事件要发生 , 她需要的所有前提也都一定发生
最大权闭合图是点权最大的闭合图
一道求最大权闭合图的题
构图:
建源点S, 汇点T
将源点S与正权点连边 , 容量是点权
将汇点T与负权点连边 , 容量是点权
将题目描述的边连上 , 容量是无限大
解决:
闭合图的权为所有正权点减去该图中割的容量
那么 , 很显然割越小答案越大
所以就要去求最小鸽 , 也就是最大流
这样 , 那道NOI题也就好做了
1 #include<cstdio> 2 #include<queue> 3 #include<cstring> 4 #include<cstdlib> 5 #include<iostream> 6 #include<algorithm> 7 #define APART puts("----------------------") 8 #define debug 1 9 #define FILETEST 0 10 #define inf 120010 11 #define ll long long 12 #define ha 998244353 13 #define INF 0x7fffffff 14 #define INF_T 9223372036854775807 15 #define DEBUG printf("%s %d\n",__FUNCTION__,__LINE__) 16 17 namespace chino{ 18 19 inline void setting(){ 20 #if FILETEST 21 freopen("test.in", "r", stdin); 22 freopen("test.me.out", "w", stdout); 23 #endif 24 return; 25 } 26 27 inline int read(){ 28 char c = getchar(), up = c; int num = 0; 29 for(; c < '0' || c > '9'; up = c, c = getchar()); 30 for(; c >= '0' && c <= '9'; num = (num << 3) + (num << 1) + (c ^ '0'), c = getchar()); 31 return up == '-' ? -num : num; 32 } 33 34 int n, m; 35 int S, T; 36 int maxflow, anssum; 37 int cntE = -1; 38 int head[inf]; 39 int cur[inf]; 40 int dep[inf]; 41 struct Edge{ 42 int to; 43 int flow; 44 int next; 45 }e[inf << 1]; 46 std::queue <int> Q; 47 48 inline void AddEdge(int from, int to, int flow){ 49 ++cntE; 50 e[cntE].to = to; 51 e[cntE].flow = flow; 52 e[cntE].next = head[from]; 53 head[from] = cntE; 54 return; 55 } 56 57 inline bool BFS(int s, int t){ 58 memset(dep, -1, sizeof dep); 59 memcpy(cur, head, sizeof cur); 60 while(!Q.empty()) 61 Q.pop(); 62 Q.push(s); 63 dep[s] = 0; 64 while(!Q.empty()){ 65 int x = Q.front(); 66 Q.pop(); 67 for(int i = head[x]; i ^ -1; i = e[i].next){ 68 int y = e[i].to; 69 if(dep[y] == -1 && e[i].flow){ 70 dep[y] = dep[x] + 1; 71 Q.push(y); 72 } 73 } 74 } 75 return dep[t] ^ -1; 76 } 77 78 int DFS(int s, int limit){ 79 if(limit == 0 || s == T) 80 return limit; 81 int flow = 0; 82 int f = 0; 83 for(int i = cur[s]; i ^ -1; i = e[i].next){ 84 cur[s] = i; 85 int to = e[i].to; 86 int Min = std::min(limit, e[i].flow); 87 if(dep[to] == dep[s] + 1 && (f = DFS(to, Min))){ 88 flow += f; 89 limit -= f; 90 e[i].flow -= f; 91 e[i ^ 1].flow += f; 92 if(limit == 0) 93 break; 94 } 95 } 96 return flow; 97 } 98 99 inline void dinic(int s, int t){ 100 while(BFS(s, t)) 101 maxflow += DFS(s, INF); 102 return; 103 } 104 105 inline int main(){ 106 memset(head, -1, sizeof head); 107 n = read(); 108 m = read(); 109 S = n + m + 1; 110 T = n + m + 2; 111 for(int i = 1; i <= n; i++){ 112 int tmp = read(); 113 AddEdge(i + m, T, tmp); 114 AddEdge(T, i + m, 0); 115 } 116 for(int i = 1; i <= m; i++){ 117 int a = read(); 118 int b = read(); 119 int v = read(); 120 anssum += v; 121 AddEdge(i, a + m, INF >> 1); 122 AddEdge(a + m, i, 0); 123 124 AddEdge(i, b + m, INF >> 1); 125 AddEdge(b + m, i, 0); 126 127 AddEdge(S, i, v); 128 AddEdge(i, S, 0); 129 } 130 dinic(S, T); 131 printf("%d\n", anssum - maxflow); 132 return 0; 133 } 134 135 }//namespace chino 136 137 int main(){return chino::main();}
例题2:luogu太空飞行计划问题
题意重点:每个仪器在购买后可以用无限次
并不是说一个实验收入是负数就不要做这个实验
如题目样例
有些实验可能是给后面的实验买仪器 , 可能这个实验不赚钱 , 但是后面的实验不需要买仪器了 , 那这个方案可能更优\
不难看出也是一道最大权闭合图题 , 所以建图就是如之前一样了
那么怎么样输出方案呐
因为最小鸽C[S,T]将图G分成没有交的两个点集S和T
而答案就是集合S里所有的点
为什么?
因为这张图中间的边的边权是无限大 , 所以这些边永远不会在鸽里面
所以其实鸽就是在选择剩下的两种边----S连向实验 和 仪器连向T
选择连接实验的边就等于说不选这个实验
而选择仪器的边就等于说选这个实验
另外怎么样去判断在S集合的点呐?
再搜一遍?
dinic的BFS返回false的条件就是无法遍历到T , 所以最后一遍BFS后dep不是初始值的点就是与S联通的
1 #include<cmath> 2 #include<queue> 3 #include<cstdio> 4 #include<cstring> 5 #include<cstdlib> 6 #include<iostream> 7 #include<algorithm> 8 #define APART puts("----------------------") 9 #define debug 1 10 #define FILETEST 0 11 #define inf 1010 12 #define ll long long 13 #define ha 998244353 14 #define INF 0x7fffffff 15 #define INF_T 9223372036854775807 16 #define DEBUG printf("%s %d\n",__FUNCTION__,__LINE__) 17 18 namespace chino{ 19 20 inline void setting(){ 21 #if FILETEST 22 freopen("test.in", "r", stdin); 23 freopen("test.me.out", "w", stdout); 24 #endif 25 return; 26 } 27 28 inline int read(int &num){ 29 num = 0; char c = getchar(), up = c; 30 for(; c < '0' || c > '9'; up = c, c = getchar()); 31 for(; c >= '0' && c <= '9'; num = (num << 3) + (num << 1) + (c ^ '0'), c = getchar()); 32 if(up == '-') num = -num; 33 return c == '\r' || c == '\n'; 34 } 35 36 int n, m; 37 int cntE = -1; 38 int S, T; 39 int maxflow, anssum; 40 int head[inf]; 41 int cur[inf]; 42 int dep[inf]; 43 struct Edge{ 44 int to; 45 int flow; 46 int next; 47 }e[inf << 1]; 48 std::queue <int> Q; 49 50 inline void AddEdge(int from, int to, int val){ 51 // printf("AddEdge: x = %d, y = %d, val = %d\n", from, to, val); 52 ++cntE; 53 e[cntE].to = to; 54 e[cntE].flow = val; 55 e[cntE].next = head[from]; 56 head[from] = cntE; 57 return; 58 } 59 60 inline bool BFS(int s, int t){ 61 memset(dep, -1, sizeof dep); 62 memcpy(cur, head, sizeof cur); 63 while(!Q.empty()) 64 Q.pop(); 65 dep[s] = 0; 66 Q.push(s); 67 while(!Q.empty()){ 68 int x = Q.front(); 69 Q.pop(); 70 for(int i = head[x]; i ^ -1; i = e[i].next){ 71 int y = e[i].to; 72 if(dep[y] == -1 && e[i].flow){ 73 dep[y] = dep[x] + 1; 74 Q.push(y); 75 } 76 } 77 } 78 return dep[t] ^ -1; 79 } 80 81 int DFS(int s, int limit){ 82 if(limit == 0 || s == T) 83 return limit; 84 int flow = 0; 85 int f = 0; 86 for(int i = cur[s]; i ^ -1; i = e[i].next){ 87 int to = e[i].to; 88 int Min = std::min(limit, e[i].flow); 89 cur[s] = i; 90 if(dep[s] == dep[to] - 1 && (f = DFS(to, Min))){ 91 flow += f; 92 limit -= f; 93 e[i].flow -= f; 94 e[i ^ 1].flow += f; 95 if(limit == 0) 96 break; 97 } 98 } 99 return flow; 100 } 101 102 inline void dinic(int s, int t){ 103 while(BFS(s, t)) 104 maxflow += DFS(s, INF); 105 return; 106 } 107 108 inline int main(){ 109 memset(head, -1, sizeof head); 110 read(n); 111 read(m); 112 S = n + m + 1; 113 T = n + m + 2; 114 for(int i = 1; i <= n; i++){ 115 int val; 116 if(read(val)){ 117 anssum += val; 118 AddEdge(S, i, val); 119 AddEdge(i, S, 0); 120 continue; 121 } 122 anssum += val; 123 AddEdge(S, i, val); 124 AddEdge(i, S, 0); 125 while(read(val) == 0){ 126 AddEdge(i, val + n, INF >> 1); 127 AddEdge(val + n, i, 0); 128 } 129 AddEdge(i, val + n, INF >> 1); 130 AddEdge(val + n, i, 0); 131 } 132 for(int i = 1; i <= m; i++){ 133 int tmp; 134 read(tmp); 135 AddEdge(i + n, T, tmp); 136 AddEdge(T, i + n, 0); 137 } 138 dinic(S, T); 139 for(int i = 1; i <= n; i++){ 140 if(dep[i] ^ -1) 141 printf("%d ", i); 142 } puts(""); 143 for(int i = n + 1; i <= n + m; i++){ 144 if(dep[i] ^ -1) 145 printf("%d ", i - n); 146 } puts(""); 147 printf("%d\n", anssum - maxflow); 148 return 0; 149 } 150 151 }//namespace chino 152 153 int main(){return chino::main();}