说“大全”,其实也不见得全。。。
1、最大流,Dinic 模板。[LOJ#101]
这题 Dinic 需要卡常才能过,主要是 BFS 从汇点开始搜更快。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> #include <algorithm> using namespace std; #define rep(i, s, t) for(int i = (s); i <= (t); i++) #define dwn(i, s, t) for(int i = (s); i >= (t); i--) int read() { int x = 0, f = 1; char c = getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); } return x * f; } #define maxn 1000010 #define maxm 8000010 #define oo 2147483647 #define LL long long struct Edge { int from, to, flow; Edge() {} Edge(int _1, int _2, int _3): from(_1), to(_2), flow(_3) {} }; struct Dinic { int n, m, s, t, head[maxn], nxt[maxm]; Edge es[maxm]; int vis[maxn], Q[maxn], hd, tl; int cur[maxn]; void init() { m = 0; memset(head, -1, sizeof(head)); return ; } void setn(int _) { n = _; return ; } void AddEdge(int a, int b, int c) { es[m] = Edge(a, b, c); nxt[m] = head[a]; head[a] = m++; es[m] = Edge(b, a, 0); nxt[m] = head[b]; head[b] = m++; return ; } bool BFS() { memset(vis, 0, sizeof(vis)); vis[t] = 1; hd = tl = 0; Q[++tl] = t; while(hd < tl) { int u = Q[++hd]; for(int i = head[u]; i != -1; i = nxt[i]) { Edge& e = es[i^1]; if(!vis[e.from] && e.flow) { vis[e.from] = vis[u] + 1; Q[++tl] = e.from; } } } return vis[s] > 0; } LL DFS(int u, int a) { if(u == t || !a) return a; LL flow = 0, f; for(int& i = cur[u]; i != -1; i = nxt[i]) { Edge& e = es[i]; if(vis[e.to] == vis[u] - 1 && (f = DFS(e.to, min(a, e.flow)))) { flow += f; a -= f; e.flow -= f; es[i^1].flow += f; if(!a) return flow; } } return flow; } LL MaxFlow(int _s, int _t) { s = _s; t = _t; LL flow = 0; while(BFS()) { rep(i, 1, n) cur[i] = head[i]; flow += DFS(s, oo); } return flow; } } sol; int main() { int n = read(), m = read(), s = read(), t = read(); sol.init(); rep(i, 1, m) { int u = read(), v = read(), c = read(); sol.AddEdge(u, v, c); } sol.setn(n); printf("%lld\n", sol.MaxFlow(s, t)); return 0; }
2、最小费用最大流,ZKW 模板。[LOJ#102]
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> #include <algorithm> #include <queue> using namespace std; #define rep(i, s, t) for(int i = (s); i <= (t); i++) #define dwn(i, s, t) for(int i = (s); i >= (t); i--) int read() { int x = 0, f = 1; char c = getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); } return x * f; } #define maxn 410 #define maxm 30010 #define oo 2147483647 struct Edge { int from, to, flow, cost; Edge() {} Edge(int _1, int _2, int _3, int _4): from(_1), to(_2), flow(_3), cost(_4) {} }; struct ZKW { int s, t, cost, ans, n, m, head[maxn], nxt[maxm]; Edge es[maxm]; int d[maxn]; deque <int> Q; bool inq[maxn]; bool vis[maxn]; void init() { m = 0; memset(head, -1, sizeof(head)); return ; } void setn(int _) { n = _; return ; } void AddEdge(int a, int b, int c, int d) { es[m] = Edge(a, b, c, d); nxt[m] = head[a]; head[a] = m++; es[m] = Edge(b, a, 0, -d); nxt[m] = head[b]; head[b] = m++; return ; } bool BFS() { rep(i, 1, n) d[i] = oo; Q.push_back(t); inq[t] = 1; d[t] = 0; while(!Q.empty()) { int u = Q.front(); Q.pop_front(); inq[u] = 0; for(int i = head[u]; i != -1; i = nxt[i]) { Edge& e = es[i^1]; if(d[e.from] > d[u] + e.cost && e.flow) { d[e.from] = d[u] + e.cost; if(!inq[e.from]) { inq[e.from] = 1; if(Q.empty() || d[Q.front()] >= d[e.from]) Q.push_front(e.from); else Q.push_back(e.from); } } } } if(d[s] == oo) return 0; cost = d[s]; return 1; } int DFS(int u, int a) { if(u == t || !a){ ans += cost * a; return a; } if(vis[u]) return 0; vis[u] = 1; int flow = 0, f; for(int i = head[u]; i != -1; i = nxt[i]) { Edge& e = es[i]; if(d[e.to] == d[u] - e.cost && e.flow && (f = DFS(e.to, min(e.flow, a)))) { flow += f; a -= f; e.flow -= f; es[i^1].flow += f; if(!a) return flow; } } return flow; } int MaxFlow(int _s, int _t) { s = _s; t = _t; int flow = 0, f; while(BFS()) do { memset(vis, 0, sizeof(vis)); f = DFS(s, oo); flow += f; } while(f); return flow; } } sol; int main() { int n = read(), m = read(); sol.init(); rep(i, 1, m) { int a = read(), b = read(), c = read(), d = read(); sol.AddEdge(a, b, c, d); } sol.setn(n); printf("%d ", sol.MaxFlow(1, n)); printf("%d\n", sol.ans); return 0; }
3、无源汇有上下界可行流。[LOJ#115]
先强行让每条边流成下界,那么现在可能有一些点出入不平衡, 需要调整。假设节点 \(u\) 出大于入,那么从 \(u\) 向新建汇点连一条出流 - 入流的边;假设节点 \(v\) 入大于出,那么从新建源点向 \(v\) 连一条入流 - 出流的边。(因为我们需要通过原图将流量调至平衡,所以出多反而需要连出边,入多需要连入边)
如果新图最大流不能把源点出发(或到达汇点)的流量流满,就说明无解。若有解,则最终每条边的流量 = 流量下界 + 新图中对应边的流量。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> #include <algorithm> using namespace std; #define rep(i, s, t) for(int i = (s); i <= (t); i++) #define dwn(i, s, t) for(int i = (s); i >= (t); i--) int read() { int x = 0, f = 1; char c = getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); } return x * f; } #define maxn 210 #define maxm 61210 #define oo 2147483647 struct Edge { int from, to, flow; Edge() {} Edge(int _1, int _2, int _3): from(_1), to(_2), flow(_3) {} }; struct Dinic { int n, m, s, t, head[maxn], nxt[maxm]; Edge es[maxm]; int vis[maxn], Q[maxn], hd, tl; int cur[maxn]; void init() { m = 0; memset(head, -1, sizeof(head)); return ; } void setn(int _) { n = _; return ; } void AddEdge(int a, int b, int c) { es[m] = Edge(a, b, c); nxt[m] = head[a]; head[a] = m++; es[m] = Edge(b, a, 0); nxt[m] = head[b]; head[b] = m++; return ; } bool BFS() { memset(vis, 0, sizeof(vis)); vis[t] = 1; hd = tl = 0; Q[++tl] = t; while(hd < tl) { int u = Q[++hd]; for(int i = head[u]; i != -1; i = nxt[i]) { Edge& e = es[i^1]; if(!vis[e.from] && e.flow) { vis[e.from] = vis[u] + 1; Q[++tl] = e.from; } } } return vis[s] > 0; } int DFS(int u, int a) { if(u == t || !a) return a; int flow = 0, f; for(int& i = cur[u]; i != -1; i = nxt[i]) { Edge& e = es[i]; if(vis[e.to] == vis[u] - 1 && (f = DFS(e.to, min(a, e.flow)))) { flow += f; a -= f; e.flow -= f; es[i^1].flow += f; if(!a) return flow; } } return flow; } int MaxFlow(int _s, int _t) { s = _s; t = _t; int flow = 0; while(BFS()) { rep(i, 1, n) cur[i] = head[i]; flow += DFS(s, oo); } return flow; } } sol; int eid[maxm], low[maxm], in_f[maxn], out_f[maxn]; int main() { int n = read(), m = read(), s = n + 1, t = n + 2, sum = 0; sol.init(); sol.setn(t); rep(i, 1, m) { int u = read(), v = read(), l = read(), r = read(); if(l > r) return puts("NO"), 0; eid[i] = sol.m; low[i] = l; sol.AddEdge(u, v, r - l); in_f[v] += l; out_f[u] += l; } rep(i, 1, n) if(in_f[i] > out_f[i]) sol.AddEdge(s, i, in_f[i] - out_f[i]), sum += in_f[i] - out_f[i]; else if(out_f[i] > in_f[i]) sol.AddEdge(i, t, out_f[i] - in_f[i]); if(sol.MaxFlow(s, t) < sum) return puts("NO"), 0; puts("YES"); rep(i, 1, m) printf("%d\n", low[i] + sol.es[eid[i]^1].flow); return 0; }
4、有源汇有上下界最大流。[LOJ#116]
从原图汇点向原图源点加一条容量无穷下界为 \(0\) 的边 \(e\),求出可行流,(若存在可行流)然后从原图中的源点到原图汇点跑一遍最大流就是答案。
有两点小问题:
- 新建的源点、汇点是否会影响最后那一遍最大流?
A:不会的,因为如果存在解,那么新建的边就是满流的,即它们都指向源,或从汇指出,不肯能通过他们形成增广路。
- 为什么直接跑最大流就是答案,不需要加上求完可行流后 \(e\) 上的流量吗?
A:不需要,因为第二遍最大流会把第一次在 \(e\) 上跑出的流量自动退回去——它会自动给你把这部分答案算上。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> #include <algorithm> using namespace std; #define rep(i, s, t) for(int i = (s); i <= (t); i++) #define dwn(i, s, t) for(int i = (s); i >= (t); i--) int read() { int x = 0, f = 1; char c = getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); } return x * f; } #define maxn 212 #define maxm 20412 #define oo 2147483647 struct Edge { int from, to, flow; Edge() {} Edge(int _1, int _2, int _3): from(_1), to(_2), flow(_3) {} }; struct Dinic { int n, m, s, t, head[maxn], nxt[maxm]; Edge es[maxm]; int vis[maxn], Q[maxn], hd, tl; int cur[maxn]; void init() { m = 0; memset(head, -1, sizeof(head)); return ; } void setn(int _) { n = _; return ; } void AddEdge(int a, int b, int c) { es[m] = Edge(a, b, c); nxt[m] = head[a]; head[a] = m++; es[m] = Edge(b, a, 0); nxt[m] = head[b]; head[b] = m++; return ; } bool BFS() { memset(vis, 0, sizeof(vis)); vis[t] = 1; hd = tl = 0; Q[++tl] = t; while(hd < tl) { int u = Q[++hd]; for(int i = head[u]; i != -1; i = nxt[i]) { Edge& e = es[i^1]; if(!vis[e.from] && e.flow) { vis[e.from] = vis[u] + 1; Q[++tl] = e.from; } } } return vis[s] > 0; } int DFS(int u, int a) { if(u == t || !a) return a; int flow = 0, f; for(int& i = cur[u]; i != -1; i = nxt[i]) { Edge& e = es[i]; if(vis[e.to] == vis[u] - 1 && (f = DFS(e.to, min(a, e.flow)))) { flow += f; a -= f; e.flow -= f; es[i^1].flow += f; if(!a) return flow; } } return flow; } int MaxFlow(int _s, int _t) { s = _s; t = _t; int flow = 0; while(BFS()) { rep(i, 1, n) cur[i] = head[i]; flow += DFS(s, oo); } return flow; } } sol; int eid[maxm], low[maxm], in_f[maxn], out_f[maxn]; int main() { int n = read(), m = read(), S = read(), T = read(), s = n + 1, t = n + 2, sum = 0; sol.init(); sol.setn(t); rep(i, 1, m) { int u = read(), v = read(), l = read(), r = read(); if(l > r) return puts("please go home to sleep"), 0; eid[i] = sol.m; low[i] = l; sol.AddEdge(u, v, r - l); in_f[v] += l; out_f[u] += l; } sol.AddEdge(T, S, oo); rep(i, 1, n) if(in_f[i] > out_f[i]) sol.AddEdge(s, i, in_f[i] - out_f[i]), sum += in_f[i] - out_f[i]; else if(out_f[i] > in_f[i]) sol.AddEdge(i, t, out_f[i] - in_f[i]); if(sol.MaxFlow(s, t) < sum) return puts("please go home to sleep"), 0; printf("%d\n", sol.MaxFlow(S, T)); return 0; }
5、有源汇有上下界最小流。[LOJ#117]
从原图汇点向汇点加一条容量无穷下界为 \(0\) 的边 \(e\),求出可行流,记此时边 \(e\) 上的流量为 \(flow_e\);然后删掉边 \(e\),从原图汇点向原图源点跑一遍最大流,这次最大流答案记为 \(f\),那么最终答案为 \(flow_e - f\)。
可能产生的疑惑见上一题。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> #include <algorithm> using namespace std; #define rep(i, s, t) for(int i = (s); i <= (t); i++) #define dwn(i, s, t) for(int i = (s); i >= (t); i--) int read() { int x = 0, f = 1; char c = getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); } return x * f; } #define maxn 50013 #define maxm 350022 #define oo 2147483647 struct Edge { int from, to, flow; Edge() {} Edge(int _1, int _2, int _3): from(_1), to(_2), flow(_3) {} }; struct Dinic { int n, m, s, t, head[maxn], nxt[maxm]; Edge es[maxm]; int vis[maxn], Q[maxn], hd, tl; int cur[maxn]; void init() { m = 0; memset(head, -1, sizeof(head)); return ; } void setn(int _) { n = _; return ; } void AddEdge(int a, int b, int c) { es[m] = Edge(a, b, c); nxt[m] = head[a]; head[a] = m++; es[m] = Edge(b, a, 0); nxt[m] = head[b]; head[b] = m++; return ; } bool BFS(bool block) { memset(vis, 0, sizeof(vis)); vis[t] = 1; hd = tl = 0; Q[++tl] = t; while(hd < tl) { int u = Q[++hd]; for(int i = head[u]; i != -1; i = nxt[i]) { Edge& e = es[i^1]; if(block && e.from == s && e.to == t) continue; if(!vis[e.from] && e.flow) { vis[e.from] = vis[u] + 1; Q[++tl] = e.from; } } } return vis[s] > 0; } int DFS(int u, int a, bool block) { if(u == t || !a) return a; int flow = 0, f; for(int& i = cur[u]; i != -1; i = nxt[i]) { Edge& e = es[i]; if(block && u == s && e.to == t) continue; if(vis[e.to] == vis[u] - 1 && (f = DFS(e.to, min(a, e.flow), block))) { flow += f; a -= f; e.flow -= f; es[i^1].flow += f; if(!a) return flow; } } return flow; } int MaxFlow(int _s, int _t, bool block) { s = _s; t = _t; int flow = 0; while(BFS(block)) { rep(i, 1, n) cur[i] = head[i]; flow += DFS(s, oo, block); } return flow; } } sol; int eid[maxm], low[maxm], in_f[maxn], out_f[maxn]; int main() { int n = read(), m = read(), S = read(), T = read(), s = n + 1, t = n + 2, sum = 0; sol.init(); sol.setn(t); rep(i, 1, m) { int u = read(), v = read(), l = read(), r = read(); if(l > r) return puts("please go home to sleep"), 0; eid[i] = sol.m; low[i] = l; sol.AddEdge(u, v, r - l); in_f[v] += l; out_f[u] += l; } int cure = sol.m; sol.AddEdge(T, S, oo); rep(i, 1, n) if(in_f[i] > out_f[i]) sol.AddEdge(s, i, in_f[i] - out_f[i]), sum += in_f[i] - out_f[i]; else if(out_f[i] > in_f[i]) sol.AddEdge(i, t, out_f[i] - in_f[i]); int flow = sol.MaxFlow(s, t, 0); int tmp = sol.es[cure^1].flow; if(flow < sum) return puts("please go home to sleep"), 0; printf("%d\n", tmp - sol.MaxFlow(T, S, 1)); return 0; }
6、最小费用可行流(带负环、下界)。[ZZOJ#30]
先强行所有边权为负的边流满上界,边权为正的边流成下界,然后用可行流的方法调整。注意调整时跑的是最小费用最大流,而不是最小费用流。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> #include <algorithm> #include <queue> using namespace std; #define rep(i, s, t) for(int i = (s); i <= (t); i++) #define dwn(i, s, t) for(int i = (s); i >= (t); i--) int read() { int x = 0, f = 1; char c = getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); } return x * f; } #define maxn 310 #define maxm 6010 #define oo 2147483647 #define LL long long struct Edge { int from, to, flow, cost; Edge() {} Edge(int _1, int _2, int _3, int _4): from(_1), to(_2), flow(_3), cost(_4) {} }; struct ZKW { int n, m, s, t, cost, head[maxn], nxt[maxm]; LL ans; Edge es[maxm]; int d[maxn]; bool inq[maxn]; deque <int> Q; bool vis[maxn]; void init() { m = 0; memset(head, -1, sizeof(head)); return ; } void setn(int _) { n = _; return ; } void AddEdge(int a, int b, int c, int d) { es[m] = Edge(a, b, c, d); nxt[m] = head[a]; head[a] = m++; es[m] = Edge(b, a, 0, -d); nxt[m] = head[b]; head[b] = m++; return ; } bool BFS() { rep(i, 1, n) d[i] = oo; d[t] = 0; Q.push_back(t); inq[t] = 1; while(!Q.empty()) { int u = Q.front(); Q.pop_front(); inq[u] = 0; for(int i = head[u]; i != -1; i = nxt[i]) { Edge& e = es[i^1]; if(d[e.from] > d[u] + e.cost && e.flow) { d[e.from] = d[u] + e.cost; if(!inq[e.from]) { inq[e.from] = 1; if(Q.empty() || d[e.from] <= d[Q.front()]) Q.push_front(e.from); else Q.push_back(e.from); } } } } if(d[s] == oo) return 0; cost = d[s]; return 1; } int DFS(int u, int a) { if(u == t || !a) return ans += (LL)cost * a, a; if(vis[u]) return 0; vis[u] = 1; int flow = 0, f; for(int i = head[u]; i != -1; i = nxt[i]) { Edge& e = es[i]; if(d[e.to] == d[u] - e.cost && (f = DFS(e.to, min(a, e.flow)))) { flow += f; a -= f; e.flow -= f; es[i^1].flow += f; if(!a) return flow; } } return flow; } int MaxFlow(int _s, int _t) { s = _s; t = _t; int flow = 0, f; while(BFS()) do { memset(vis, 0, sizeof(vis)); f = DFS(s, oo); flow += f; } while(f); return flow; } } sol; int in_f[maxn], out_f[maxn], eid[maxm], low[maxm]; bool rev[maxm]; int main() { int n = read(), m = read(), s = n + 1, t = s + 1; LL ans = 0; sol.init(); sol.setn(t); rep(i, 1, m) { int a = read(), b = read(), lower = read(), upper = read(), cost = read(); if(cost >= 0) { ans += (LL)lower * cost; in_f[b] += lower; out_f[a] += lower; low[i] = lower; eid[i] = sol.m; sol.AddEdge(a, b, upper - lower, cost); } if(cost < 0) { ans += (LL)upper * cost; in_f[b] += upper; out_f[a] += upper; low[i] = upper; eid[i] = sol.m; rev[i] = 1; sol.AddEdge(b, a, upper - lower, -cost); } } int need_f = 0; rep(i, 1, n) { if(in_f[i] > out_f[i]) sol.AddEdge(s, i, in_f[i] - out_f[i], 0), need_f += in_f[i] - out_f[i]; if(out_f[i] > in_f[i]) sol.AddEdge(i, t, out_f[i] - in_f[i], 0); } if(sol.MaxFlow(s, t) < need_f) return puts("QAQ"), 0; printf("%lld\n", ans + sol.ans); // rep(i, 1, m) printf("%d\n", low[i] + (sol.es[eid[i]^1].flow * (rev[i] ? -1 : 1))); return 0; }