最大权闭合图

前置芝士:最大流最小割

一. 啥是闭合图

一件事件x发生需要一个条件:

1. y事件已经发生

2. z事件已经发生

对于这样的依赖关系 , 我们可以用闭合来来描述或者解决

有向图的闭合图(closure)(来源于 胡伯涛《最小割模型在信息学竞赛中的应用》 论文 )

物理意义:事物间依赖关系:一个事件要发生 , 她需要的所有前提也都一定发生

最大权闭合图是点权最大的闭合图

例题:[NOI2006]最大获利

 一道求最大权闭合图的题

构图:

建源点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();}

 

posted @ 2019-08-24 16:23  ChiaroShiro  阅读(271)  评论(0编辑  收藏  举报