网络流模板大全

说“大全”,其实也不见得全。。。

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),求出可行流,(若存在可行流)然后从原图中的源点到原图汇点跑一遍最大流就是答案。

有两点小问题:

  1. 新建的源点、汇点是否会影响最后那一遍最大流?

A:不会的,因为如果存在解,那么新建的边就是满流的,即它们都指向源,或从汇指出,不肯能通过他们形成增广路。

  1. 为什么直接跑最大流就是答案,不需要加上求完可行流后 (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;
}
原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/7900457.html