有上下界的网络流题目泛做
题目1 ZOJ2314 无源汇可行流
题目大意:
给一张有向图,每条边有容量上界可容量下界,求是否有可行流?
如果有,输出每条边的流量。
算法讨论:
首先,计算出每个点的M(i)值,就是流入下界和-流出下界和,如果M(i)小于0,就从i向T连-M(i)的边,如果M(i)大于0,就从S向i连M(i)的边。
同时原图中的边的容量为上界减下界。跑一遍从S到T的最大流。
判断是否有可行流的方法:如果所有与S相连的出边都满流,则说明有可行流。否则没有。
每条边流量的值 = 这条边的流量下界 + 其反向边的流量。
代码:
#include <cstdlib> #include <cstring> #include <iostream> #include <algorithm> #include <cstdio> #include <vector> using namespace std; const int N = 200 + 5; const int M = 40000 + 5; const int oo = 0x3f3f3f3f; int n, m; int d[N], low[M]; struct Edge { int from, to, cap, flow; Edge(int u = 0, int v = 0, int cp = 0, int fw = 0): from(u), to(v), cap(cp), flow(fw) {} }; struct Dinic { int n, mm, s, t; int dis[N], cur[N], que[N * 5]; bool vis[N]; vector <Edge> edges; vector <int> G[N]; void clear() { for(int i = 0; i <= n; ++ i) G[i].clear(); edges.clear(); } void insert(int from, int to, int cap) { edges.push_back((Edge){from, to, cap, 0}); edges.push_back((Edge){to, from, 0, 0}); mm = edges.size(); G[from].push_back(mm - 2); G[to].push_back(mm - 1); } bool bfs() { int head = 1, tail = 1; memset(vis, false, sizeof vis); dis[s] = 0; vis[s] = true; que[head] = s; while(head <= tail) { int x = que[head]; for(int i = 0; i < (signed) G[x].size(); ++ i) { Edge &e = edges[G[x][i]]; if(!vis[e.to] && e.cap > e.flow) { vis[e.to] = true; dis[e.to] = dis[x] + 1; que[++ tail] = e.to; } } ++ head; } return vis[t]; } int dfs(int x, int a) { if(x == t || a == 0) return a; int flw = 0, f; for(int &i = cur[x]; i < (signed) G[x].size(); ++ i) { Edge &e = edges[G[x][i]]; if(dis[e.to] == dis[x] + 1 && (f = dfs(e.to, min(a, e.cap - e.flow))) > 0) { e.flow += f; edges[G[x][i] ^ 1].flow -= f; a -= f; flw += f; if(a == 0) break; } } return flw; } void mx(int s, int t) { this->s = s; this->t = t; int flw = 0; while(bfs()) { memset(cur, 0, sizeof cur); flw += dfs(s, oo); } } void getans() { bool flag = true; for(int i = 0; i < (signed) G[0].size(); ++ i) { Edge e = edges[G[0][i]]; if(e.cap - e.flow > 0) flag = false; } if(!flag) puts("NO"); else { puts("YES"); for(int i = 0; i < m * 2; i += 2) { printf("%d\n", low[(i + 2) >> 1] + edges[i ^ 1].cap - edges[i ^ 1].flow); } } } }net; int main() { int t, u, v, l, r; bool flag = false; scanf("%d", &t); while(t --) { if(flag) puts(""); else flag = true; scanf("%d%d", &n, &m); net.clear(); net.n = n + 1; for(int i = 1; i <= m; ++ i) { scanf("%d%d%d%d", &u, &v, &l, &r); d[u] -= l; d[v] += l; low[i] = l; net.insert(u, v, r - l); } for(int i = 1; i <= n; ++ i) { if(d[i] > 0) net.insert(0, i, d[i]); else if(d[i] < 0) net.insert(i, n + 1, -d[i]); } net.mx(0, n + 1); net.getans(); memset(d, 0, sizeof d); } return 0; }
题目2 SGU 176 有源汇最小流
题目大意:
给一张图和每条边的一个容量上限,还有一个参数,如果参数是1,则要求这条边必须满流,否则不做要求,求1->n最小流。
算法讨论:
如果必须满流,则是上下界都是流量,如果不做要求,则下界是0,上界是流量。
转化后,先求出每个点的M(i)值,对于M(i)的连边还是像上面一样,同时原图中上下界都是容量的边则不用连边了,下界是0的连容量为流量的边。
如此连边后,从超级源点S向超级汇点T跑一次MaxFlow,然后加一条从n->1流量为正无穷的边,再从S向T流一次MaxFlow。
对于答案,先判断是否有可行流,就是与超级源直接相连的边全部满流,说明有可行流。
那么最小流的流量就是从n->1反向边的流量。
那么每个边的流量要么是其容量,要么是其相反边的流量。所以对于每条边要记录一个编号,方便更新答案时使用。
代码:
/* 以此代码纪念我们的相识。 河北 鹿泉一中 NOC竞赛 */ #include <cstdlib> #include <iostream> #include <cstring> #include <cstdio> #include <algorithm> #include <vector> using namespace std; const int N = 100 + 5; const int M = 10000 + 5; const int oo = 0x3f3f3f3f; int n, m, d[N], tmp; int ans[M]; struct Edge { int from, to, cap, flow, id; Edge(int u=0, int v=0, int cp=0, int fw=0, int id=0): from(u), to(v), cap(cp), flow(fw), id(id) {} }; struct Dinic { int n, mm, s, t; int dis[N], que[N * 100], cur[N]; bool vis[N]; vector <Edge> edges; vector <int> G[N]; void add(int from, int to, int cap, int id) { edges.push_back(Edge(from, to, cap, 0, id)); edges.push_back(Edge(to, from, 0, 0, 0)); mm = edges.size(); G[from].push_back(mm - 2); G[to].push_back(mm - 1); } bool bfs() { int head = 1, tail = 1; memset(vis, false, (n + 2) * sizeof (bool)); dis[s] = 0; vis[s] = true; que[head] = s; while(head <= tail) { int x = que[head]; for(int i = 0; i < (signed)G[x].size(); ++ i) { Edge &e = edges[G[x][i]]; if(!vis[e.to] && e.cap > e.flow) { vis[e.to] = true; dis[e.to] = dis[x] + 1; que[++ tail] = e.to; } } ++ head; } return vis[t]; } int dfs(int x, int a) { if(x == t || a == 0) return a; int flw = 0, f; for(int &i = cur[x]; i < (signed) G[x].size(); ++ i) { Edge &e = edges[G[x][i]]; if(dis[e.to] == dis[x] + 1 && (f = dfs(e.to, min(a, e.cap - e.flow))) > 0) { e.flow += f; edges[G[x][i] ^ 1].flow -= f; a -= f; flw += f; if(a == 0) break; } } return flw; } int mx(int s, int t) { this->s = s; this->t = t; int flw = 0; while(bfs()) { memset(cur, 0, sizeof cur); flw += dfs(s, oo); } return flw; } void getans() { bool flag = false; for(int i = 0; i < (signed) G[0].size(); ++ i) { Edge e = edges[G[0][i]]; if(e.cap - e.flow > 0) { flag = true; break; } } if(flag) puts("Impossible"); else { for(int i = 0; i < (signed) G[n - 1].size(); ++ i) { Edge e = edges[G[n - 1][i]]; if(e.to == 1) { printf("%d\n", edges[G[n - 1][i] ^ 1].cap - edges[G[n - 1][i] ^ 1].flow); break; } } for(int i = 0; i < 2 * tmp; i += 2) { ans[edges[i].id] = edges[i ^ 1].cap - edges[i ^ 1].flow; } for(int i = 1; i <= m; ++ i) { if(i - 1) printf(" %d", ans[i]); else printf("%d", ans[i]); } } } }net; int main() { int u, v, c, type; scanf("%d%d", &n, &m); net.n = n + 1; for(int i = 1; i <= m; ++ i) { scanf("%d%d%d%d", &u, &v, &c, &type); if(type) { d[u] -= c; d[v] += c; ans[i] = c; } else net.add(u, v, c, i), ++ tmp; } for(int i = 1; i <= n; ++ i) { if(d[i] < 0) net.add(i, n + 1, -d[i], 0); else if(d[i] > 0) net.add(0, i, d[i], 0); } net.mx(0, n + 1); net.add(n, 1, oo, 0); net.mx(0, n + 1); net.getans(); return 0; }