说“大全”,其实也不见得全。。。
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
", 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
", 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
", 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
", 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
", 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
", ans + sol.ans);
// rep(i, 1, m) printf("%d
", low[i] + (sol.es[eid[i]^1].flow * (rev[i] ? -1 : 1)));
return 0;
}