三种上下界网络流模板

  1 /*
  2     首先建立一个源S和一个汇T,一般称为附加源和附加汇。
  3     对于图中的每条弧<u,v>,假设它容量上界为c,下界b,那么把这条边拆为三条只有上界的弧。
  4     一条为<S,v>,容量为b;
  5   一条为<u,T>,容量为b;
  6   一条为<u,v>,容量为c-b。
  7     其中前两条弧一般称为附加弧。
  8     然后对这张图跑最大流,以S为源,以T为汇,如果所有的附加弧都满流,则原图有可行流;否则就是无解。 
  9     这时,每条非附加弧的流量加上它的容量下界,就是原图中这条弧应该有的流量。
 10     
 11     对于原图中的每条弧,我们把c-b称为它的自由流量,意思就是只要它流满了下界,这些流多少都没问题。
 12   既然如此,对于每条弧<u,v>,我们强制给v提供b单位的流量,并且强制从u那里拿走b单位的流量,这一步对应着两条附加弧。
 13   如果这一系列强制操作能完成的话,也就是有一组可行流了。
 14   注意:这张图的最大流只是对应着原图的一组可行流,而不是原图的最大或最小流。
 15 */
 16 #include <bits/stdc++.h>
 17 using namespace std;
 18 const int oo = (1LL << 31) - 1;
 19 const int LEN = 1e5 + 5;
 20 struct node {
 21     int x, y, l, r;
 22 } a[LEN];
 23 namespace ISAP {
 24     int flow, tot, n, m, src, tar, qh, qt, cnt, ans;
 25     struct edge {
 26         int vet, next, len;
 27     } E[LEN * 2];
 28     int dis[LEN], gap[LEN], head[LEN], cur[LEN], q[LEN], vis[LEN], IN[LEN];
 29     void add(int u, int v, int c) {
 30         E[++tot] = (edge){v, head[u], c};
 31         head[u] = tot;
 32     }
 33     void join(int u, int v, int c) {
 34         add(u, v, c);
 35         add(v, u, 0);
 36     }
 37     void bfs(int s) {
 38         qh = qt = 0;
 39         q[++qt] = s;
 40         dis[s] = 0;
 41         vis[s] = 1;
 42         while (qh < qt) {
 43             int u = q[++qh];
 44             gap[dis[u]]++;
 45             for (int e = head[u]; e != -1; e = E[e].next) {
 46                 int v = E[e].vet;
 47                 if (E[e ^ 1].len && !vis[v]) {
 48                     dis[v] = dis[u] + 1;
 49                     vis[v] = 1;
 50                     q[++qt] = v;
 51                 }
 52             }
 53         }
 54     }
 55     int isap(int u, int aug) {
 56         if (u == tar) return aug;
 57         int flow = 0;
 58         for (int e = head[u]; e != -1; e = E[e].next) {
 59             int v = E[e].vet;
 60             if (E[e].len && dis[v] == dis[u] - 1) {
 61                 int tmp = isap(v, min(aug - flow, E[e].len));
 62                 E[e].len -= tmp;
 63                 E[e ^ 1].len += tmp;
 64                 flow += tmp;
 65                 head[u] = e;
 66                 if (flow == aug || dis[src] == cnt) return flow;
 67             }
 68         }
 69         if (!--gap[dis[u]++]) dis[src] = cnt;
 70         ++gap[dis[u]];
 71         head[u] = cur[u];
 72         return flow;
 73     }
 74     void init() {
 75         tot = -1, gap[0] = 0;
 76         for (int i = 1; i <= cnt; i++) {
 77             dis[i] = gap[i] = vis[i] = IN[i] = 0;
 78             head[i] = -1;
 79         }
 80     }
 81     int maxflow(int s, int t) {
 82         src = s, tar = t;
 83         int res = 0;
 84         for (int i = 1; i <= cnt; i++) cur[i] = head[i];
 85         bfs(tar);
 86         while (dis[src] < cnt) res += isap(src, oo);
 87         return res;
 88     }
 89 }
 90 using namespace ISAP;
 91 int main() {
 92     scanf("%d %d", &n, &m);
 93     cnt = n;
 94     src = ++cnt, tar = ++cnt;
 95     init();
 96     for (int i = 1; i <= m; i++) {
 97         int x, y, l, r;
 98         scanf("%d %d %d %d", &x, &y, &l, &r);
 99         a[i] = (node){x, y, l, r};
100         join(x, y, r - l);
101         IN[y] += l, IN[x] -= l;
102     }
103     for (int i = 1; i <= n; i++) {
104         if (IN[i] < 0) join(i, tar, -IN[i]);
105         else {
106             join(src, i, IN[i]);
107             flow += IN[i];
108         }
109     }
110     int ans = maxflow(src, tar);
111     if (flow == ans) {
112         puts("YES");
113         for (int i = 1; i <= m; i++) printf("%d\n", a[i].l + E[i * 2 - 1].len);
114     } else puts("NO");
115     return 0;
116 }
无源汇可行流
  1 /*
  2     先来看有源汇可行流
  3   建模方法:
  4   建立弧<t,s>,容量下界为0,上界为oo。 
  5   然后对这个新图(实际上只是比原图多了一条边)按照无源汇可行流的方法建模,
  6     如果所有附加弧满流,则存在可行流。
  7   求原图中每条边对应的实际流量的方法,同无源汇可行流,只是忽略掉弧<t,s>就好。
  8   而且这时候弧<t,s>的流量就是原图的总流量。
  9   理解方法:
 10   有源汇相比无源汇的不同就在于,源和汇是不满足流量平衡的,那么连接<t,s>之后,
 11     源和汇也满足了流量平衡,就可以直接按照无源汇的方式建模。
 12   注意:这张图的最大流只是对应着原图的一组可行流,而不是原图的最大或最小流。
 13 
 14     有源汇最大流
 15   建模方法:
 16   首先按照有源汇可行流的方法建模,如果不存在可行流,更别提什么最大流了。
 17   如果存在可行流,那么在运行过有源汇可行流的图上(就是已经存在流量的那张图,流量不要清零),
 18     跑一遍从s到t的最大流(这里的s和t是原图的源和汇,不是附加源和附加汇),就是原图的最大流。
 19   理解方法:
 20   为什么要在那个已经有了流量的图上跑最大流?因为那张图保证了每条弧的容量下界,在这张图上跑最大流,
 21     实际上就是在容量下界全部满足的前提下尽量多得获得“自由流量”。
 22   注意,在这张已经存在流量的图上,弧<t,s>也是存在流量的,千万不要忽略这条弧。
 23     因为它的相反弧<s,t>的流量为<t,s>的流量的相反数,且<s,t>的容量为0,所以这部分的流量也是会被算上的。
 24 */
 25 #include <bits/stdc++.h>
 26 using namespace std;
 27 typedef long long ll;
 28 const int LEN = 1e5 + 5;
 29 const int oo = (1LL << 31) - 1;
 30 namespace DINIC {
 31     int tot, n, m, src, tar, qh, qt, cnt, s, t, S, T;
 32     int ans, flow;
 33     struct edge {
 34         int vet, next, len;
 35     } E[LEN * 2];
 36     int dis[LEN], gap[LEN], head[LEN], cur[LEN], q[LEN], vis[LEN], IN[LEN];
 37     void add(int u, int v, int c) {
 38         E[++tot] = (edge){v, head[u], c};
 39         head[u] = tot;
 40     }
 41     void join(int u, int v, int c) {
 42         add(u, v, c);
 43         add(v, u, 0);
 44     }
 45     void init() {
 46         tot = -1;
 47         for (int i = 1; i <= cnt; i++) head[i] = -1;
 48     }
 49     bool bfs() {
 50         for (int i = 1; i <= cnt; i++) dis[i] = 0;
 51         qh = qt = 0;
 52         q[++qt] = src;
 53         dis[src] = 1;
 54         while (qh < qt) {
 55             int u = q[++qh];
 56             for (int e = head[u]; e != -1; e = E[e].next) {
 57                 int v = E[e].vet;
 58                 if (E[e].len && !dis[v]) {
 59                     dis[v] = dis[u] + 1;
 60                     if (v == tar) return 1;
 61                     q[++qt] = v;
 62                 }
 63             }
 64         }
 65         return dis[tar];
 66     }
 67     int dfs(int u, int aug) {
 68         if (u == tar || !aug) return aug;
 69         int tmp = 0;
 70         for (int &e = cur[u]; e != -1; e = E[e].next) {
 71             int v = E[e].vet;
 72             if (dis[v] == dis[u] + 1) {
 73                 if (tmp = dfs(v, min(aug, E[e].len))) {
 74                     E[e].len -= tmp;
 75                     E[e ^ 1].len += tmp;
 76                     return tmp;
 77                 }
 78             }
 79         }
 80         return 0;
 81     }
 82     int maxflow(int s, int t) {
 83         src = s, tar = t;
 84         int res = 0, flow = 0;
 85         while (bfs()) {
 86             for (int i = 1; i <= cnt; i++) cur[i] = head[i];
 87             while (flow = dfs(src, oo)) res += flow;
 88         }
 89         return res;
 90     }
 91 }
 92 using namespace DINIC;
 93 int main() {
 94     scanf("%d %d %d %d", &n, &m, &s, &t);
 95     cnt = n;
 96     S = ++cnt, T = ++cnt;
 97     init();
 98     for (int i = 1; i <= m; i++) {
 99         int x, y, l, r;
100         scanf("%d %d %d %d", &x, &y, &l, &r);
101         join(x, y, r - l);
102         IN[y] += l, IN[x] -= l;
103     } 
104     for (int i = 1; i <= n; i++) {
105         if (IN[i] < 0) join(i, T, -IN[i]);
106         else if (IN[i] > 0) {
107             flow += IN[i];
108             join(S, i, IN[i]);
109         }
110     }
111     join(t, s, oo);
112     ans = maxflow(S, T);
113     if (ans != flow) puts("please go home to sleep");
114     else {
115         ans = maxflow(s, t);
116         printf("%lld\n", ans);
117     }
118     return 0;
119 }
有源汇最大流
  1 /*
  2     先来看有源汇可行流
  3   建模方法:
  4   建立弧<t,s>,容量下界为0,上界为oo。 
  5   然后对这个新图(实际上只是比原图多了一条边)按照无源汇可行流的方法建模,
  6     如果所有附加弧满流,则存在可行流。
  7   求原图中每条边对应的实际流量的方法,同无源汇可行流,只是忽略掉弧<t,s>就好。
  8   而且这时候弧<t,s>的流量就是原图的总流量。
  9   理解方法:
 10   有源汇相比无源汇的不同就在于,源和汇是不满足流量平衡的,那么连接<t,s>之后,
 11     源和汇也满足了流量平衡,就可以直接按照无源汇的方式建模。
 12   注意:这张图的最大流只是对应着原图的一组可行流,而不是原图的最大或最小流。
 13 
 14     有源汇最小流
 15   有源汇最小流的常见建模方法比较多,我就只说我常用的一种。
 16   建模方法:
 17   首先按照有源汇可行流的方法建模,但是不要建立<t,s>这条弧。
 18   然后在这个图上,跑从附加源ss到附加汇tt的最大流。
 19   这时候再添加弧<t,s>,下界为0,上界oo。
 20   在现在的这张图上,从ss到tt的最大流,就是原图的最小流。
 21   理解方法:
 22   我们前面提到过,有源汇可行流的流量只是对应一组可行流,并不是最大或者最小流。
 23   并且在跑完有源汇可行流之后,弧<t,s>的流量就是原图的流量。
 24   从这个角度入手,我们想让弧<t,s>的流量尽量小,就要尽量多的消耗掉那些“本来不需要经过<t,s>”的流量。
 25   于是我们在添加<t,s>之前,跑一遍从ss到tt的最大流,就能尽量多的消耗那些流量啦QwQ。
 26 */
 27 #include <bits/stdc++.h>
 28 using namespace std;
 29 typedef long long ll;
 30 const int LEN = 2e5 + 5;
 31 const int oo = (1LL << 31) - 1;
 32 namespace DINIC {
 33     int tot, n, m, src, tar, qh, qt, cnt, s, t, S, T, ans, flow;
 34     struct edge {
 35         int vet, next, len;
 36     } E[LEN * 2];
 37     int dis[LEN], gap[LEN], head[LEN], cur[LEN], q[LEN], vis[LEN], IN[LEN];
 38     void add(int u, int v, int c) {
 39         E[++tot] = (edge){v, head[u], c};
 40         head[u] = tot;
 41     }
 42     void join(int u, int v, int c) {
 43         add(u, v, c);
 44         add(v, u, 0);
 45     }
 46     void init() {
 47         tot = -1;
 48         for (int i = 1; i <= cnt; i++) head[i] = -1;
 49     }
 50     bool bfs() {
 51         for (int i = 1; i <= cnt; i++) dis[i] = 0;
 52         qh = qt = 0;
 53         q[++qt] = src;
 54         dis[src] = 1;
 55         while (qh < qt) {
 56             int u = q[++qh];
 57             for (int e = head[u]; e != -1; e = E[e].next) {
 58                 int v = E[e].vet;
 59                 if (E[e].len && !dis[v]) {
 60                     dis[v] = dis[u] + 1;
 61                     if (v == tar) return 1;
 62                     q[++qt] = v;
 63                 }
 64             }
 65         }
 66         return dis[tar];
 67     }
 68     int dfs(int u, int aug) {
 69         if (u == tar || !aug) return aug;
 70         int tmp = 0;
 71         for (int &e = cur[u]; e != -1; e = E[e].next) {
 72             int v = E[e].vet;
 73             if (dis[v] == dis[u] + 1) {
 74                 if (tmp = dfs(v, min(aug, E[e].len))) {
 75                     E[e].len -= tmp;
 76                     E[e ^ 1].len += tmp;
 77                     return tmp;
 78                 }
 79             }
 80         }
 81         return 0;
 82     }
 83     int maxflow(int s, int t) {
 84         src = s, tar = t;
 85         int res = 0, flow = 0;
 86         while (bfs()) {
 87             for (int i = 1; i <= cnt; i++) cur[i] = head[i];
 88             while (flow = dfs(src, oo)) res += flow;
 89         }
 90         return res;
 91     }
 92 }
 93 using namespace DINIC;
 94 int main() {
 95     scanf("%d %d %d %d", &n, &m, &s, &t);
 96     cnt = n;
 97     S = ++cnt, T = ++cnt;
 98     init();
 99     for (int i = 1; i <= m; i++) {
100         int x, y, l, r;
101         scanf("%d %d %d %d", &x, &y, &l, &r);
102         join(x, y, r - l);
103         IN[y] += l, IN[x] -= l;
104     } 
105     for (int i = 1; i <= n; i++) {
106         if (IN[i] < 0) join(i, T, -IN[i]);
107         else if (IN[i] > 0) {
108             flow += IN[i];
109             join(S, i, IN[i]);
110         }
111     }
112     ans = maxflow(S, T);
113     flow -= ans;
114     join(t, s, oo);
115     ans = maxflow(S, T);
116     if (ans != flow) puts("please go home to sleep");
117     else printf("%d\n", ans);
118     return 0;
119 }
有源汇最小流

 

posted on 2018-08-03 19:31  NineSwords  阅读(378)  评论(0编辑  收藏  举报

导航