【笔记】网络流

感觉会分成好多篇,所以这篇的标题可能不叫网络流(
挖坑

基本模型,有向图,源点 S 汇点 T ,满足每条边的容量限制、除源汇点外的点入流等于出流

最大流

dinic 算法,算法原理什么的先放着(虽然也有用这个出题的),很多时候跑的很快,理论复杂度上界 \(O(n^2m)\) ,二分图上是 \(O(n\sqrt{m})\)
只是...你别把网络流的图潜意识里只想成二分图了...
部分想法,流就是方案的组合,流量限制可以刻画方案间的冲突关系和选择限制
最大流就是最小割,最大流跑完的残量网络是不连通的
有向图最小路径覆盖,二分图跑最大流(最大匹配)
删点令源汇点不连通,将每个点 u 拆成 ...-> u->u' ->... ,容量 1 ,跑最大流,暂时不知道也没去想为什么对了,反正这么做就过了(

#include <queue>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN = 505;
const int MAXE = 10005;
const int INF = 1e9;
int T, N, E, tote, fst[MAXN], cur[MAXN], dep[MAXN];
struct edge {
	int v, w, pre;
} e[MAXE];
void adde(int a, int b, int c, int k)
{
	e[k].v = b, e[k].w = c, e[k].pre = fst[a], fst[a] = k;
}
int bfs(int s, int t)
{
	memset(dep, 0, sizeof(dep));
	queue<int> q;
	q.push(s), dep[s] = 1, cur[s] = fst[s];
	while (!q.empty()) {
		int x = q.front(); q.pop();
		for (int o=fst[x]; o!=-1; o=e[o].pre) {
			if (e[o].w && dep[e[o].v]==0) {
				dep[e[o].v] = dep[x] + 1, cur[e[o].v] = fst[e[o].v];
				q.push(e[o].v);
			}
		}
	}
	return dep[t];
}
int dfs(int x, int t, int want)
{
	if (x==t || !want) return want;
	int flow = 0;
	for (int o=cur[x]; o!=-1; o=e[o].pre, cur[x]=o) {
		if (e[o].w> 0 && dep[e[o].v]==dep[x]+1) {
			int f = dfs(e[o].v, t, min(e[o].w, want));
			if (!f) continue;
			e[o].w -= f, e[o^1].w += f;
			want -= f, flow += f;
			if (!want) return flow;
		}
	}
	return flow;
}
int dinic(int s, int t)
{
	int ans = 0;
	while (bfs(s, t)) ans += dfs(s, t, INF);
	return ans;
}
int main()
{
	for (scanf("%d", &T); T; T--) {
		memset(fst, -1, sizeof(fst));
		scanf("%d%d", &N, &E); tote = 0;
		for (int i=0; i< E; i++) {
			int a, b, c; scanf("%d%d%d", &a, &b, &c);
			adde(a, b, c, tote++), adde(b, a, 0, tote++);
		}
		printf("%d\n", dinic(1, N));
	}
}

费用流(最小费用最大流)

zkw 算法,算法原理什么的不知道(,反正跑的挺快(

#include <queue>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN = 5005;
const int MAXE = 100005;
const int inf = 1000000000;
int T, N, E, tote, fst[MAXN], dep[MAXN], vis[MAXN], cost;
struct edge {
	int v, c, w, pre;
} e[MAXE];
void adde(int a, int b, int flow, int w, int k)
{
	e[k].v = b, e[k].c = flow, e[k].w = w, e[k].pre = fst[a], fst[a] = k;
}
int bfs(int s, int t)
{
	memset(dep, 0x3f, sizeof(dep)); int infdep = dep[s];
	// be careful if dep could exceed 0x3f
	memset(vis, 0, sizeof(vis));
	deque<int> q;
	q.push_back(t), vis[t] = 1, dep[t] = 0;
	while (!q.empty()) {
		int x = q.front(); q.pop_front();
		for (int o=fst[x]; o!=-1; o=e[o].pre) {
			if (e[o^1].c && dep[e[o].v]> dep[x]-e[o].w) {
				dep[e[o].v] = dep[x] - e[o].w;
				if (!vis[e[o].v]) {
					vis[e[o].v] = 1;
					if (!q.empty() && dep[e[o].v]< dep[q.front()]) {
						q.push_front(e[o].v);
					} else q.push_back(e[o].v);
				}
			}
		}
		vis[x] = 0;
	}
	return dep[s]< infdep;
}
int dfs(int x, int t, int want)
{
	if (x==t) return want;
	int flow = 0;
	vis[x] = 1;
	for (int o=fst[x]; o!=-1; o=e[o].pre) {
		int v = e[o].v;
		if (!vis[v] && e[o].c && dep[x]-e[o].w==dep[v]) {
			int f = dfs(v, t, min(want, e[o].c));
			if (!f) continue;
			e[o].c -= f, e[o^1].c += f;
			flow += f, want -= f;
			cost += f * e[o].w;
			if (!want) break;
		}
	}
	return flow;
}
int zkw(int s, int t)
{
	int flow = 0;
	while (bfs(s, t)) {
		vis[t] = 1;
		while (vis[t]) {
			memset(vis, 0, sizeof(vis));
			flow += dfs(s, t, inf);
		}
	}
	return flow;
}
int main()
{
	for (scanf("%d", &T); T; T--) {
		memset(fst, -1, sizeof(fst));
		tote = 0, cost = 0;
		scanf("%d%d", &N, &E);
		int s, t; s = 1, t = N;
		for (int i=1; i<=E; i++) {
			int a, b, c, w; scanf("%d%d%d%d", &a, &b, &c, &w);
			adde(a, b, c, w, tote++);
			adde(b, a, 0, -w, tote++);
		}
		int flow = zkw(s, t);
		printf("%d %d\n", flow, cost);
	}
}

[TJOI2010]打扫房间
闭合回路——度为 2 ——黑白染色,源点 -[2]-> 黑点 -[1]-> 白点 -[2]-> 汇点,判断跑满最大流

posted @ 2021-08-21 21:35  zrkc  阅读(51)  评论(0)    收藏  举报