【题目总结】网络流 (kuangbin专题11部分题解)

1.最大流

POJ1459 - Power Network

题目链接

这道题看上去很复杂,但实际上就是一个非常简单的最大流模型。我们设一个超级源点 s s s,超级汇点 t t t。从 s s s向每一个power station连接一个边权为 p [ u ] p[u] p[u]的有向边( u u u为这个power station的编号)。然后将所有consumer向 t t t连接一条边权为无穷大的有向边。中间的每一条dispatcher,都对应一条边。然后跑一个最大流即可,得到的最大流的值就是这道题目的答案。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
typedef long long LL;
const LL INF = 1e18;
const int N = 200005;
const int M = 400005;

int n, m, s, t, nc, np, en;
int front[N];

struct Edge {
    
    
	LL v, w, next;
}e[100050];

void addEdge(LL u, LL v, LL w) {
    
    
	++en;
	e[en].v = v;
	e[en].w = w;
	e[en].next = front[u];
	front[u] = en;
}

struct ISAP {
    
    
	LL maxflow, node_cnt;
	LL dep[200005], gap[200005];
	
	void bfs() {
    
    
		for (LL i = 1; i <= node_cnt; ++i) {
    
    
			dep[i] = -1; gap[i] = 0;
		}
		dep[t] = 0; gap[0] = 1;
		queue<LL> q; q.push(t);
		while (!q.empty()) {
    
    
			int u = q.front();
			q.pop();
			for (LL i = front[u]; i; i = e[i].next) {
    
    
				LL v = e[i].v;
				if (dep[v] != -1) continue;
				q.push(v); dep[v] = dep[u] + 1;
				++gap[dep[v]];
			}
		}
		return;
	}
	
	LL dfs(LL u, LL flow) {
    
    
		if (u == t) {
    
    
			maxflow += flow; return flow;
		}
		LL used = 0;
		for (LL i = front[u]; i; i = e[i].next) {
    
    
			LL d = e[i].v;
			if (e[i].w and dep[d] + 1 == dep[u]) {
    
    
				LL mi = dfs(d, min(e[i].w, flow - used));
				if (mi) {
    
    
					e[i].w -= mi; e[i ^ 1ll].w += mi;
					used += mi;
				}
				if (used == flow) return used;
			}
		}
		--gap[dep[u]];
		if (gap[dep[u]] == 0) dep[s] = node_cnt + 1;
		++dep[u]; ++gap[dep[u]];
		return used;
	}
	
	void opt() {
    
    
		maxflow = 0;
		bfs();
		while (dep[s] < node_cnt) dfs(s, INF);
	}
}isap;

void main2() {
    
    
	en = 1;
	cin >> np >> nc >> m;
	isap.node_cnt = n + 2;
	s = n + 1; t = n + 2;
	for (int i = 1; i <= n + 2; ++i) {
    
    
		front[i] = 0;
	}
	for (int i = 1; i <= m; ++i) {
    
    
		string x; cin >> x;
		int len = x.length();
		LL u, v, w, tmp = 0;
		for (int j = 0; j < len; ++j) {
    
    
			if (x[j] == '(') continue;
			else if (x[j] == ',') {
    
    
				u = tmp + 1;
				tmp = 0;
			}
			else if (x[j] == ')') {
    
    
				v = tmp + 1;
				tmp = 0;
			}
			else tmp = tmp * 10ll + x[j] - '0';
		}
		w = tmp;
		addEdge(u, v, w);
		addEdge(v, u, 0ll);
	}
	for (int i = 1; i <= np; ++i) {
    
    
		string x; cin >> x;
		int len = x.length();
		LL v, w, tmp = 0;
		for (int j = 0; j < len; ++j) {
    
    
			if (x[j] == '(') continue;
			else if (x[j] == ')') {
    
    
				v = tmp + 1;
				tmp = 0;
			}
			else tmp = tmp * 10ll + x[j] - '0'; 
		}
		w = tmp;
		addEdge(s, v, w);
		addEdge(v, s, 0ll);
	}
	for (int i = 1; i <= nc; ++i) {
    
    
		string x; cin >> x;
		int len = x.length();
		LL u, w, tmp = 0;
		for (int j = 0; j < len; ++j) {
    
    
			if (x[j] == '(') continue;
			else if (x[j] == ')') {
    
    
				u = tmp + 1;
				tmp = 0;
			}
			else tmp = tmp * 10ll + x[j] - '0';
		}
		w = tmp;
		addEdge(u, t, w);
		addEdge(t, u, 0ll);
	}
	isap.opt();
	cout << isap.maxflow << '\n';
}

int main() {
    
    
//	ios::sync_with_stdio(false);
//	cin.tie(0); cout.tie(0);
	LL _ = 1;
//	cin >> _;
	while (cin >> n) main2();
	return 0;
}

HDU4280 - Island Transport

题目链接

基础的最大流板子题。

根据读入的所有点坐标,将这些点按照横坐标进行排序。这样就可以确定 s s s点和 t t t点了。然后剩下的就是读入每一条路径,将每一条路径 ( u , v ) (u,v) (u,v)分别建立 u → v u\rightarrow v uv v → u v\rightarrow u vu两条正边以及其对应的反边,然后跑一个最大流即可。

最大流的值即为答案。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL INF = 1e18; 

int n, m, s, t, en;
int front[100005];

struct Edge {
    
    
	LL v, w, next;
}e[1000050];

void addEdge(LL u, LL v, LL w) {
    
    
	e[++en] = {
    
    v, w, front[u]};
	front[u] = en;
}

struct ISAP {
    
    
	LL maxflow, node_cnt;
	LL dep[100050], gap[100050];
	
	void bfs() {
    
    
		for (LL i = 1; i <= node_cnt; ++i) {
    
    
			dep[i] = -1; gap[i] = 0;
		}
		dep[t] = 0; gap[0] = 1;
		queue<LL> q; q.push(t);
		while (!q.empty()) {
    
    
			int u = q.front();
			q.pop();
			for (LL i = front[u]; i; i = e[i].next) {
    
    
				LL v = e[i].v;
				if (dep[v] != -1) continue;
				q.push(v); dep[v] = dep[u] + 1;
				++gap[dep[v]];
			}
		}
		return;
	}
	
	LL dfs(LL u, LL flow) {
    
    
		if (u == t) {
    
    
			maxflow += flow; return flow;
		}
		LL used = 0;
		for (LL i = front[u]; i; i = e[i].next) {
    
    
			LL d = e[i].v;
			if (e[i].w and dep[d] + 1 == dep[u]) {
    
    
				LL mi = dfs(d, min(e[i].w, flow - used));
				if (mi) {
    
    
					e[i].w -= mi; e[i ^ 1ll].w += mi;
					used += mi;
				}
				if (used == flow) return used;
			}
		}
		--gap[dep[u]];
		if (gap[dep[u]] == 0) dep[s] = node_cnt + 1;
		++dep[u]; ++gap[dep[u]];
		return used;
	}
	
	void opt() {
    
    
		maxflow = 0;
		node_cnt = n;
		bfs();
		while (dep[s] < node_cnt) dfs(s, INF);
	}
}isap;

struct POINT {
    
    
	int x, y, id;
}a[100005];

void main2() {
    
    
	cin >> n >> m;
	for (int i = 1; i <= n; ++i) {
    
    
		front[i] = 0;
	}
	en = 1;
	for (int i = 1; i <= n; ++i) {
    
    
		cin >> a[i].x >> a[i].y;
		a[i].id = i;
	}
	sort(a + 1, a + n + 1, [](const POINT &A, const POINT &B) {
    
    
		return (A.x < B.x);
	});
	s = a[1].id; t = a[n].id;
	for (int i = 1; i <= m; ++i) {
    
    
		int x, y, z;
		cin >> x >> y >> z;
		addEdge(x, y, z);
		addEdge(y, x, 0);
		addEdge(y, x, z);
		addEdge(x, y, 0);
	}
	isap.opt();
	cout << isap.maxflow << '\n';
}

int main() {
    
    
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	LL _ = 1;
	cin >> _;
	while (_--) main2();
	return 0;
}

POJ3436 - ACM Computer Factory

题目链接

每一个点的始末状态都不相同,所以我们要将每一个点都进行拆点,即用 i i i表示第 i i i个点的初始状态,用 i + n i+n i+n表示第 i i i个点的终止状态。

接下来我们根据题目中的过程描述进行连边。

连的边有两种:

  1. 不同点之间的关联。如果 u + n , v u+n,v u+n,v两个点之间应当连一条有向边,当且仅当 u u u的终止状态可以被 v v v的初始状态所接受。
  2. 同一个点从初始状态到终止状态的过程转移。

不难想到,连接的所有边的边权都是 1 1 1

题目要求是求出单位时间内的最大产能即最大产能的方案,其实就是求这个网络的最大流和构成最大流的流的构成。

求解最大流的值得算法有很多,有dinic,有ISAP等,直接套板子即可。

在求完网络流后求解最大流的方案的方法是:遍历残量网络,如果所连的反边经过的流大于 0 0 0,则意味着这条反边对应的正向边被选中,且反边的权值即为正边所流过的流量。

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<vector>
#include<queue>
using namespace std;
typedef long long LL; 
const LL INF = 1e18;

int p, n, en, s, t;
int front[100005], per[100005];
vector<int> st[100005], ed[100005];

struct TUPLE {
    
    
	int x, y, z;
};

struct Edge {
    
    
	LL u, v, w, next;
}e[100050];

void addEdge(LL u, LL v, LL w) {
    
    
	++en;
	e[en].u = u;
	e[en].v = v;
	e[en].w = w;
	e[en].next = front[u];
	front[u] = en;
}

struct ISAP {
    
    
	LL maxflow, node_cnt;
	LL dep[550], gap[550];
	
	void bfs() {
    
    
		for (LL i = 1; i <= node_cnt; ++i) {
    
    
			dep[i] = -1; gap[i] = 0;
		}
		dep[t] = 0; gap[0] = 1;
		queue<LL> q; q.push(t);
		while (!q.empty()) {
    
    
			int u = q.front();
			q.pop();
			for (LL i = front[u]; i; i = e[i].next) {
    
    
				LL v = e[i].v;
				if (dep[v] != -1) continue;
				q.push(v); dep[v] = dep[u] + 1;
				++gap[dep[v]];
			}
		}
		return;
	}
	
	LL dfs(LL u, LL flow) {
    
    
		if (u == t) {
    
    
			maxflow += flow; return flow;
		}
		LL used = 0;
		for (LL i = front[u]; i; i = e[i].next) {
    
    
			LL d = e[i].v;
			if (e[i].w and dep[d] + 1 == dep[u]) {
    
    
				LL mi = dfs(d, min(e[i].w, flow - used));
				if (mi) {
    
    
					e[i].w -= mi; e[i ^ 1ll].w += mi;
					used += mi;
				}
				if (used == flow) return used;
			}
		}
		--gap[dep[u]];
		if (gap[dep[u]] == 0) dep[s] = node_cnt + 1;
		++dep[u]; ++gap[dep[u]];
		return used;
	}
	
	void opt() {
    
    
		maxflow = 0;
		node_cnt = n * 2 + 2;
		bfs();
		while (dep[s] < node_cnt) dfs(s, INF);
	}
}isap;

void main2() {
    
    
	cin >> n;
	for (int i = 1; i <= n; ++i) {
    
    
		st[i].clear();
		ed[i].clear();
	}
	for (int i = 1; i <= n * 2 + 2; ++i) {
    
    
		front[i] = per[i] = 0;
	}
	en = 1;
	t = 2 * n + 2;
	s = 2 * n + 1;
	for (int i = 1; i <= n; ++i) {
    
    
		cin >> per[i];
		int all = 1;
		for (int j = 1; j <= p; ++j) {
    
    
			int x; cin >> x;
			st[i].push_back(x);
			if (x == 1) all = 0;
		}
		if (all) {
    
    
			addEdge(s, i, INF);
			addEdge(i, s, 0);
		}
		all = 1;
		for (int j = 1; j <= p; ++j) {
    
    
			int x; cin >> x;
			ed[i].push_back(x);
			if (x == 0) all = 0;
		}
		if (all) {
    
    
			addEdge(i + n, t, INF);
			addEdge(t, i + n, 0);
		}
	}
	int cnt2 = en;
	for (int i = 1; i <= n; ++i) {
    
    
		for (int j = 1; j <= n; ++j) {
    
    
			if (i == j) continue;
			int ok = 1;
			for (int k = 0; k < p; ++k) {
    
    
				if (st[j][k] == 2) continue;
				if (st[j][k] != ed[i][k]) {
    
    
					ok = 0;
					break;
				}
			}
			if (ok) {
    
    
				addEdge(i + n, j, min(per[i], per[j]));
				addEdge(j, i + n, 0);
			}
		}
	}
	for (int i = 1; i <= n; ++i) {
    
    
		addEdge(i, i + n, per[i]);
		addEdge(i + n, i, 0);
	}
	isap.opt();
	int ecnt = 0;
	vector<TUPLE> ans;
	for (int i = cnt2 + 1; i <= en; ++i) {
    
    
		int u = e[i].u, v = e[i].v, w = e[i].w;
		if (v > n and w > 0 and abs(u - v) != n) {
    
    
			++ecnt;
			TUPLE tmp;
			tmp.x = v - n;
			tmp.y = u;
			tmp.z = w;
			ans.push_back(tmp);
		}
	}
	cout << isap.maxflow << ' ' << ecnt << '\n';
	for (int i = 0; i < ans.size(); ++i) {
    
    
		cout << ans[i].x << ' ' << ans[i].y << ' ' << ans[i].z << '\n';
	}
}

int main() {
    
    
//	freopen("Fin.in", "r", stdin);
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	LL _ = 1;
//	cin >> _;
	while (cin >> p) main2();
	return 0;
}

POJ3281 - Dining

题目链接

考查建图。建立一个超级源点 s s s,建立一个超级汇点 t t t

考虑到每一头牛都只能喝到一种饮料,所以我们可以先让牛匹配食物,然后再匹配饮料。

首先,从超级源点向所有的食物点连一条有向边,边权为 1 1 1
然后进行食物与牛的匹配,根据牛的喜好,对于每一头牛,从其所有喜欢的食物的食物点到这头牛连一条边权为 1 1 1的有向边。
此时可能会存在牛的点的入度大于 1 1 1的情况,所以对牛进行拆点控制流量,即每一头牛用两个点来表示,这两个点之间连接一条边权为 1 1 1的有向边。对于这两个点,为了方便后面描述,描述为牛的入点指向出点。
然后匹配牛和饮料,对于每一头牛,我们从这头牛的出点向所有其喜欢的饮料连接一条边权为 1 1 1的有向边。
最后将所有的饮料点向 t t t连接一条边权为 1 1 1的有向边。

可以发现我们这次的建图,从每一个饮料点,如果有流量从这个点指向 t t t,说明这个饮料被一个有食物吃的牛匹配了。那么超级汇点 t t t得到的流量数就是牛、食物、饮料的匹配数。

所以对于这张建好的图跑一个最大流就是答案。

#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
using namespace std;
typedef long long LL;
const LL INF = 1e18;

LL n, f, dr, en, s, t;
LL front[10005];
vector<LL> food[105], drink[105];

struct Edge {
    
    
	LL v, w, next;
}e[40005];

void addEdge(LL u, LL v, LL w) {
    
    
	++en;
	e[en].v = v;
	e[en].w = w;
	e[en].next = front[u];
	front[u] = en;
}

struct ISAP {
    
    
	LL maxflow, node_cnt;
	LL dep[100050], gap[100050];
	
	void bfs() {
    
    
		for (LL i = 1; i <= node_cnt; ++i) {
    
    
			dep[i] = -1; gap[i] = 0;
		}
		dep[t] = 0; gap[0] = 1;
		queue<LL> q; q.push(t);
		while (!q.empty()) {
    
    
			int u = q.front();
			q.pop();
			for (LL i = front[u]; i; i = e[i].next) {
    
    
				LL v = e[i].v;
				if (dep[v] != -1) continue;
				q.push(v); dep[v] = dep[u] + 1;
				++gap[dep[v]];
			}
		}
		return;
	}
	
	LL dfs(LL u, LL flow) {
    
    
		if (u == t) {
    
    
			maxflow += flow; return flow;
		}
		LL used = 0;
		for (LL i = front[u]; i; i = e[i].next) {
    
    
			LL d = e[i].v;
			if (e[i].w and dep[d] + 1 == dep[u]) {
    
    
				LL mi = dfs(d, min(e[i].w, flow - used));
				if (mi) {
    
    
					e[i].w -= mi; e[i ^ 1ll].w += mi;
					used += mi;
				}
				if (used == flow) return used;
			}
		}
		--gap[dep[u]];
		if (gap[dep[u]] == 0) dep[s] = node_cnt + 1;
		++dep[u]; ++gap[dep[u]];
		return used;
	}
	
	void opt() {
    
    
		maxflow = 0;
		node_cnt = n * 2 + f + dr + 2;
		bfs();
		while (dep[s] < node_cnt) dfs(s, INF);
	}
}isap;

void main2() {
    
    
	cin >> n >> f >> dr;
	s = n * 2 + f + dr + 1;
	t = n * 2 + f + dr + 2;
	en = 1;
	for (int i = 1; i <= t; ++i) {
    
    
		front[i] = 0;
	}
	for (int i = 1; i <= n; ++i) {
    
    
		food[i].clear();
		drink[i].clear();
	}
	for (int i = 1; i <= f; ++i) {
    
    
		addEdge(s, i, 1);
		addEdge(i, s, 0);
	}
	for (int i = 1; i <= n; ++i) {
    
    
		int x, y;
		cin >> x >> y;
		for (int j = 1; j <= x; ++j) {
    
    
			int z; cin >> z;
			food[i].push_back(z);
		}
		for (int j = 1; j <= y; ++j) {
    
    
			int z; cin >> z;
			drink[i].push_back(z);
		}
	} 
	for (int i = 1; i <= n; ++i) {
    
    
		for (int j = 0; j < food[i].size(); ++j) {
    
    
			addEdge(food[i][j], i + f, 1);
			addEdge(i + f, food[i][j], 0);
		}
	}
	for (int i = 1; i <= n; ++i) {
    
    
		addEdge(i + f, i + n + f, 1);
		addEdge(i + n + f, i + f, 0);
	}
	for (int i = 1; i <= n; ++i) {
    
    
		for (int j = 0; j < drink[i].size(); ++j) {
    
    
			addEdge(i + n + f, n * 2 + f + drink[i][j], 1);
			addEdge(n * 2 + f + drink[i][j], i + n + f, 0);
		}
	}
	for (int i = 1; i <= dr; ++i) {
    
    
		addEdge(n * 2 + f + i, t, 1);
		addEdge(t, n * 2 + f + i, 0);
	}
	isap.opt();
	cout << isap.maxflow << '\n';
}

int main() {
    
    
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	LL _ = 1;
//	cin >> _;
	while (_--) main2();
	return 0;
}

HDU4292 - Food

题目链接

这道题和POJ3281几乎一样,只不过饮料和食物的数量是通过输入给进来的,我们只需要根据输入的食物和饮料的数量改变一下从超级源点 s s s到食物、从饮料到超级汇点 t t t的边权即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;

const LL INF = 1e14;

LL front[100005];
LL en = 1;

LL n, m, f, d, s, t;

struct Edge {
    
    
	LL v, w, next;
}e[400050];

void addEdge(LL u, LL v, LL w) {
    
    
	++en;
	e[en].v = v;
	e[en].w = w;
	e[en].next = front[u];
	front[u] = en;
}

struct ISAP {
    
    
	LL maxflow, node_cnt;
	LL dep[100005], gap[100005];
	
	void bfs() {
    
    
		for (LL i = 1; i <= node_cnt; ++i) {
    
    
			dep[i] = -1; gap[i] = 0;
		}
		dep[t] = 0; gap[0] = 1;
		queue<LL> q; q.push(t);
		while (!q.empty()) {
    
    
			int u = q.front();
			q.pop();
			for (LL i = front[u]; i; i = e[i].next) {
    
    
				LL v = e[i].v;
				if (dep[v] != -1) continue;
				q.push(v); dep[v] = dep[u] + 1;
				++gap[dep[v]];
			}
		}
		return;
	}
	
	LL dfs(LL u, LL flow) {
    
    
		if (u == t) {
    
    
			maxflow += flow; return flow;
		}
		LL used = 0;
		for (LL i = front[u]; i; i = e[i].next) {
    
    
			LL d = e[i].v;
			if (e[i].w and dep[d] + 1 == dep[u]) {
    
    
				LL mi = dfs(d, min(e[i].w, flow - used));
				if (mi) {
    
    
					e[i].w -= mi; e[i ^ 1ll].w += mi;
					used += mi;
				}
				if (used == flow) return used;
			}
		}
		--gap[dep[u]];
		if (gap[dep[u]] == 0) dep[s] = node_cnt + 1;
		++dep[u]; ++gap[dep[u]];
		return used;
	}
	
	void opt() {
    
    
		maxflow = 0;
		bfs();
		while (dep[s] < node_cnt) dfs(s, INF);
	}
}isap;

vector<int> F, D;

void main2() {
    
    
	cin >> f >> d;
	isap.node_cnt = n * 2 + f + d + 2;
	t = isap.node_cnt;
	s = t - 1;
	for (int i = 1; i <= t; ++i) {
    
    
		front[i] = 0;
	}
	en = 1;
	F.clear(); D.clear();
	for (int i = 1; i <= f; ++i) {
    
    
		int x; cin >> x;
		F.push_back(x);
	}
	for (int i = 1; i <= d; ++i) {
    
    
		int x; cin >> x;
		D.push_back(x);
	}
	for (int i = 1; i <= f; ++i) {
    
    
		addEdge(s, i, F[i - 1]);
		addEdge(i, s, 0);
	}
	for (int i = 1; i <= n; ++i) {
    
    
		string x;
		cin >> x;
		for (int j = 0; j < f; ++j) {
    
    
			if (x[j] == 'Y') {
    
    
				addEdge(j + 1, f + i, 1);
				addEdge(f + i, j + 1, 0);
			}
		}
	}
	for (int i = 1; i <= n; ++i) {
    
    
		addEdge(i + f, i + n + f, 1);
		addEdge(i + n + f, i + f, 0);
	}
	for (int i = 1; i <= n; ++i) {
    
    
		string x;
		cin >> x;
		for (int j = 0; j < d; ++j) {
    
    
			if (x[j] == 'Y') {
    
    
				addEdge(i + n + f, j + 1 + n * 2 + f, 1);
				addEdge(j + 1 + n * 2 + f, i + n + f, 0);
			}
		}
	}
	for (int i = 1; i <= d; ++i) {
    
    
		addEdge(i + n * 2 + f, t, D[i - 1]);
		addEdge(t, i + n * 2 + f, 0);
	}
	isap.opt();
	cout << isap.maxflow << '\n';
}

int main() {
    
    
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	LL _ = 1;
//	cin >> _;
	while (cin >> n) main2();
	return 0;
}

POJ1087 - A Plug for UNIX

题目链接

初始限制条件是前面给的所有的插座各 1 1 1个,所以我们将所有种类的插座排成一列放在超级源点 s s s的后面,然后从 s s s向所有有的插座连一条边权为 1 1 1的有向边。

然后考虑适配器,每有一个适配器都意味着 A , B A,B A,B之间的转化,即从 A A A B B B连接一条边权为 I N F INF INF的有向边。

最后考虑需要使用插座的插头。我们要求最多有多少有插座用的插头,那么就将所有插头点向超级汇点连接一条边权为 1 1 1的有向边,这样求得最大流就是答案。然后将每个插头对应的插座向这个插头连接一条边权为 I N F INF INF的有向边。

最后跑一个网络流即可。

#include<iostream>
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<map>
using namespace std;
typedef long long LL;
const LL INF = 1e18;

LL n, m, k, s, t, cnt, en;
LL front[100005];

struct Edge {
    
    
	LL v, w, next;
}e[100050];

void addEdge(LL u, LL v, LL w) {
    
    
	++en;
	e[en].v = v;
	e[en].w = w;
	e[en].next = front[u];
	front[u] = en;
}

struct ISAP {
    
    
	LL maxflow, node_cnt;
	LL dep[100050], gap[100050];
	
	void bfs() {
    
    
		for (LL i = 1; i <= node_cnt; ++i) {
    
    
			dep[i] = -1; gap[i] = 0;
		}
		dep[t] = 0; gap[0] = 1;
		queue<LL> q; q.push(t);
		while (!q.empty()) {
    
    
			int u = q.front();
			q.pop();
			for (LL i = front[u]; i; i = e[i].next) {
    
    
				LL v = e[i].v;
				if (dep[v] != -1) continue;
				q.push(v); dep[v] = dep[u] + 1;
				++gap[dep[v]];
			}
		}
		return;
	}
	
	LL dfs(LL u, LL flow) {
    
    
		if (u == t) {
    
    
			maxflow += flow; return flow;
		}
		LL used = 0;
		for (LL i = front[u]; i; i = e[i].next) {
    
    
			LL d = e[i].v;
			if (e[i].w and dep[d] + 1 == dep[u]) {
    
    
				LL mi = dfs(d, min(e[i].w, flow - used));
				if (mi) {
    
    
					e[i].w -= mi; e[i ^ 1ll].w += mi;
					used += mi;
				}
				if (used == flow) return used;
			}
		}
		--gap[dep[u]];
		if (gap[dep[u]] == 0) dep[s] = node_cnt + 1;
		++dep[u]; ++gap[dep[u]];
		return used;
	}
	
	void opt() {
    
    
		maxflow = 0;
		node_cnt = cnt;
		bfs();
		while (dep[s] < node_cnt) dfs(s, INF);
	}
}isap;

int ss[100005], mps[100005];
int si;
map<LL, int> mp;

LL calc(string &x) {
    
    
	LL hsh = 0;
	int len = x.length();
	for (int i = 0; i < len; ++i) {
    
    
		hsh = hsh * 31ll + x[i];
	}
	return hsh;
}

vector<int> sa, sb;
vector<pair<int, int> > sc;

void main2() {
    
    
	si = 0; en = 1; cnt = 0;
	cin >> n;
	for (int i = 1; i <= n; ++i) {
    
    
		string x;
		cin >> x;
		LL hsh = calc(x);
		if (!mp[hsh]) {
    
    
			ss[++si] = hsh;
			mp[hsh] = si;
		}
		sa.push_back(mp[hsh]);
	}
	cin >> m;
	for (int i = 1; i <= m; ++i) {
    
    
		string x;
		cin >> x;
		cin >> x;
		LL hsh = calc(x);
		if (!mp[hsh]) {
    
    
			ss[++si] = hsh;
			mp[hsh] = si;
		}
		sb.push_back(mp[hsh]);
	}
	cin >> k;
	for (int i = 1; i <= k; ++i) {
    
    
		string x, y;
		cin >> x >> y;
		LL hsh;
		int tmpx, tmpy;
		hsh = calc(x);
		if (!mp[hsh]) {
    
    
			ss[++si] = hsh;
			mp[hsh] = si;
		}
		tmpx = mp[hsh];
		hsh = calc(y);
		if (!mp[hsh]) {
    
    
			ss[++si] = hsh;
			mp[hsh] = si;
		}
		tmpy = mp[hsh];
		sc.push_back(make_pair(tmpx, tmpy));
	}
	s = si * 2 + 1;
	t = cnt = si * 2 + 2;
	for (int i = 1; i <= si; ++i) {
    
    
		mps[i] = 0;
	}
	for (int i = 0; i < sb.size(); ++i) {
    
    
		++mps[sb[i]];
	} 
	for (int i = 1; i <= si; ++i) {
    
    
		if (mps[i] > 0) {
    
    
			addEdge(s, i, mps[i]);
			addEdge(i, s, 0);
		}
	}
	for (int i = 1; i <= si; ++i) {
    
    
		mps[i] = 0;
	} 
	for (int i = 0; i < sa.size(); ++i) {
    
    
		++mps[sa[i]];
	}
	for (int i = 1; i <= si; ++i) {
    
    
		if (mps[i] > 0) {
    
    
			addEdge(i + si, t, mps[i]);
			addEdge(t, i + si, 0);
		}
	}
	for (int i = 0; i < sc.size(); ++i) {
    
    
		int x = sc[i].first + si, y = sc[i].second + si;
		addEdge(x, y, INF);
		addEdge(y, x, 0);
	}
	for (int i = 1; i <= si; ++i) {
    
    
		addEdge(i, i + si, INF);
		addEdge(i + si, i, 0);
	}
	isap.opt();
	cout << m - isap.maxflow << '\n';
}

int main() {
    
    
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	LL _ = 1;
//	cin >> _;
	while (_--) main2();
	return 0;
}

HDU2732 - Leapin’ Lizards

题目链接

求最少的没有跳出网格的蜥蜴,等同于求总共的蜥蜴数量减去最多跳出网格的数量。考虑网络流求最大数量。

由于网格不大,所以我们可以考虑将网格的所有点都当做图的一个点。
首先,将所有蜥蜴的初始位置的点找出来,从超级源点 s s s向这些点连一条边权为 1 1 1的有向边。
题目中高度的含义是指,经过这个点的次数。换句话说,我们可以把这个量当做经过这个点的容量,点权通过拆点化为边权,即将网格上的每一个点都拆成两个点,中间用一条边权为这个点的高度的有向边连接。
然后考虑每个点跳出网格的过程。我们枚举网格上的每一个点,并枚举每一个点的所有和这个点的曼哈顿距离小于等于 d d d的点。如果找到的点不在网格上,那么将终点设为超级汇点 t t t,表征从这个点跳可以跳出网格。否则那个点就是终点,于是从这个点像找到的每一个终点连一条边权为无穷大的有向边。这里之所以边权设置为无穷大,是因为我们不知道从这里跳出网格外的蜥蜴有多少只,我们也不需要在这里加以限制,所以直接无穷大即可。

最后建好的图,跑一个从超级源点 s s s到超级汇点 t t t的最大流即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL INF = 1e18;

LL front[100005];
LL en = 1;

LL n, m, s, t, cnt, d;

struct Edge {
    
    
	LL v, w, next;
}e[200050];

void addEdge(LL u, LL v, LL w) {
    
    
	++en;
	e[en].v = v;
	e[en].w = w;
	e[en].next = front[u];
	front[u] = en;
}

struct ISAP {
    
    
	LL maxflow, node_cnt;
	LL dep[100005], gap[100005];
	
	void bfs() {
    
    
		for (LL i = 1; i <= node_cnt; ++i) {
    
    
			dep[i] = -1; gap[i] = 0;
		}
		dep[t] = 0; gap[0] = 1;
		queue<LL> q; q.push(t);
		while (!q.empty()) {
    
    
			int u = q.front();
			q.pop();
			for (LL i = front[u]; i; i = e[i].next) {
    
    
				LL v = e[i].v;
				if (dep[v] != -1) continue;
				q.push(v); dep[v] = dep[u] + 1;
				++gap[dep[v]];
			}
		}
		return;
	}
	
	LL dfs(LL u, LL flow) {
    
    
		if (u == t) {
    
    
			maxflow += flow; return flow;
		}
		LL used = 0;
		for (LL i = front[u]; i; i = e[i].next) {
    
    
			LL d = e[i].v;
			if (e[i].w and dep[d] + 1 == dep[u]) {
    
    
				LL mi = dfs(d, min(e[i].w, flow - used));
				if (mi) {
    
    
					e[i].w -= mi; e[i ^ 1ll].w += mi;
					used += mi;
				}
				if (used == flow) return used;
			}
		}
		--gap[dep[u]];
		if (gap[dep[u]] == 0) dep[s] = node_cnt + 1;
		++dep[u]; ++gap[dep[u]];
		return used;
	}
	
	void opt() {
    
    
		maxflow = 0;
		bfs();
		while (dep[s] < node_cnt) dfs(s, INF);
	}
}isap;

int h[25][25];

int id(int x, int y, int in_node) {
    
    
	int ret = (x - 1) * m + y;
	if (in_node == 0) ret += n * m;
	return ret;
}

void ADD(int x, int y) {
    
    
	int cur = id(x, y, 0);
	for (int i = -d; i <= d; ++i) {
    
    
		int step = d - abs(i);
		for (int j = -step; j <= step; ++j) {
    
    
			int xx = x + i, yy, tar;
			yy = y - j;
			tar = id(xx, yy, 1);
			if (xx < 1 or xx > n or yy < 1 or yy > m) {
    
    
				tar = t;
			}
			addEdge(cur, tar, INF);
			addEdge(tar, cur, 0);
			yy = y + j;
			tar = id(xx, yy, 1);
			if (xx < 1 or xx > n or yy < 1 or yy > m) {
    
    
				tar = t;
			}
			addEdge(cur, tar, INF);
			addEdge(tar, cur, 0);
		}
	}
}

int main2() {
    
    
	cin >> n >> d;
	cnt = 0;
	en = 1;
	for (int i = 1; i <= n; ++i) {
    
    
		string x;
		cin >> x;
		m = x.length();
		for (int j = 0; j < m; ++j) {
    
    
			h[i][j + 1] = x[j] - '0';
		}
	}
	for (int i = 1; i <= n * m * 2 + 2; ++i) {
    
    
		front[i] = 0;
	}
	isap.node_cnt = n * m * 2 + 2;
	s = n * m * 2 + 1;
	t = s + 1;
	for (int i = 1; i <= n; ++i) {
    
    
		string x;
		cin >> x;
		for (int j = 0; j < m; ++j) {
    
    
			if (x[j] == 'L') {
    
    
				addEdge(s, id(i, j + 1, 1), 1);
				addEdge(id(i, j + 1, 1), s, 0);
				++cnt;
			}
		}
	}
	for (int i = 1; i <= n; ++i) {
    
    
		for (int j = 1; j <= m; ++j) {
    
    
			ADD(i, j);
		}
	}
	for (int i = 1; i <= n; ++i) {
    
    
		for (int j = 1; j <= m; ++j) {
    
    
			if (h[i][j] == 0) continue;
			addEdge(id(i, j, 1), id(i, j, 0), h[i][j]);
			addEdge(id(i, j, 0), id(i, j, 1), 0);
		}
	}
	isap.opt();
//	cout << cnt - isap.maxflow << '\n';
	return (cnt - isap.maxflow);
}

int main() {
    
    
//	freopen("Fin.in", "r", stdin);
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	LL _ = 1;
	cin >> _;
	for (int i = 1; i <= _; ++i) {
    
    
		cout << "Case #" << i << ": ";
		int ans = main2();
		if (ans == 0) cout << "no ";
		else cout << ans << ' ';
		if (ans < 2) cout << "lizard was ";
		else cout << "lizards were ";
		cout << "left behind.\n";
	}
	return 0;
}

2.最小费用最大流

POJ2195 - Going Home

题目链接

由于人和房子的数量一定,而且数量非常小,根据每一个人都有可能去向所有的房子,所以我们 n 2 n^2 n2把所有的人和所有的房子连在一起,从人到房子连接一条带有距离为费用的、边权为 1 1 1的有向边。

然后我们从超级源点向所有的人连接一条费用为 0 0 0,边权为无穷大的有向边。
然后我们从所有房子向超级汇点连接一条费用为 0 0 0,边权为无穷大的有向边。

然后跑一个最小费用最大流,得到的最小费用就是答案。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
typedef long long LL;
const LL INF = 1e18;
const int N = 5005;
const int M = 50005;

int s, t, en;
int front[N], vis[N];

struct Edge {
    
    
	LL v, w, c, next;
}e[M * 4];

void addEdge(LL u, LL v, LL w, LL c) {
    
    
	++en;
	e[en].v = v;
	e[en].w = w;
	e[en].c = c;
	e[en].next = front[u];
	front[u] = en;
}

struct MCMF {
    
    
	LL dis[N], cur[N];
	LL node_cnt, maxflow, mincost;
	
	bool spfa(int _s, int _t) {
    
    
		for (int i = 1; i <= node_cnt; ++i) {
    
    
			dis[i] = INF;
			cur[i] = front[i];
		}
		queue<LL> q;
		q.push(_s); dis[_s] = 0; vis[_s] = 1;
		while (!q.empty()) {
    
    
			int u = q.front();
			q.pop();
			vis[u] = 0;
			for (LL i = front[u]; i; i = e[i].next) {
    
    
				LL v = e[i].v, w = e[i].w, c = e[i].c;
				if (w and dis[v] > dis[u] + c) {
    
    
					dis[v] = dis[u] + c;
					if (!vis[v]) {
    
    
						q.push(v); vis[v] = 1;
					}
				}
			}
		}
		return (dis[_t] != INF);
	}
	
	LL dfs(LL u, LL _t, LL flow) {
    
    
		if (u == _t) return flow;
		vis[u] = 1;
		LL ans = 0;
		for (LL &i = cur[u]; i and ans < flow; i = e[i].next) {
    
    
			LL v = e[i].v, w = e[i].w, c = e[i].c;
			if (!vis[v] and w and dis[v] == dis[u] + c) {
    
    
				LL x = dfs(v, _t, min(w, flow - ans));
				if (x) {
    
    
					mincost += x * c;
					e[i].w -= x;
					e[i ^ 1].w += x;
					ans += x;
				}
			}
		}
		vis[u] = 0;
		return ans;
	}
	
	void opt(int _s, int _t) {
    
    
		maxflow = mincost = 0;
		while (spfa(_s, _t)) {
    
    
			int x;
			while ((x = dfs(_s, _t, INF))) {
    
    
				maxflow += x;
			}
		}
	}
}fl; 

int n, m;
vector<pair<int, int> > per, hou;

void main2() {
    
    
	per.clear();
	hou.clear();
	en = 1;
	for (int i = 1; i <= n; ++i) {
    
    
		string s;
		cin >> s;
		for (int j = 0; j < m; ++j) {
    
    
			if (s[j] == 'm') per.push_back(make_pair(i, j + 1));
			if (s[j] == 'H') hou.push_back(make_pair(i, j + 1));
		}
	}
	fl.node_cnt = per.size() + hou.size() + 2;
	s = fl.node_cnt - 1;
	t = fl.node_cnt;
	for (int i = 0; i < per.size(); ++i) {
    
    
		for (int j = 0; j < hou.size(); ++j) {
    
    
			int x1 = per[i].first, y1 = per[i].second;
			int x2 = hou[j].first, y2 = hou[j].second;
			LL d = abs(x1 - x2) + abs(y1 - y2);
			addEdge(i + 1, j + 1 + per.size(), 1ll, d);
			addEdge(j + 1 + per.size(), i + 1, 0ll, -d);
		}
	}
	for (int i = 0; i < per.size(); ++i) {
    
    
		addEdge(s, i + 1, 1ll, 0ll);
		addEdge(i + 1, s, 0ll, 0ll);
	}
	for (int i = 0; i < hou.size(); ++i) {
    
    
		addEdge(i + per.size() + 1, t, 1ll, 0ll);
		addEdge(t, i + per.size() + 1, 0ll, 0ll);
	}
	fl.opt(s, t);
	cout << fl.mincost << '\n';
	for (int i = 1; i <= fl.node_cnt; ++i) {
    
    
		front[i] = 0;
	}
}

int main() {
    
    
//	freopen("Fin.in", "r", stdin);
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	LL _ = 1;
//	cin >> _;
	while (1) {
    
    
		cin >> n >> m;
		if (!n and !m) break;
		main2();
	}
	return 0;
}

POJ2516 - Minimum Cost

题目链接

由于费用是按照每一个货物计算的,所以如果按照我们之前的建边方式,一条边考虑一次性运输多个货物,那么此时这条边上的费用在最大流情况中如果没有留满,就会导致费用计算错误。

既然一个物品对应一个费用,发现每一个货物的量最多是 3 3 3,所以我们可以考虑对每一个货物进行建边。那么根据题目的意思,给每一个货物进行建边。从一个供应商到一个卖家,要连接这样的边的数量,可以认为是供应商可以提供的这个商品的数量。就算供应商可以提供的数量大于卖家所需要的数量,那么可以在卖家的后面再加上卖家所需要的量的限制。

接下来的问题是,商品不止一种。如果我们将所有的商品都建到一个图里面,那么这个图会变得相当复杂。我们发现,每一种货物之间对于答案的贡献是互不影响的,所以我们可以对于每一种货物单独建图,单独跑最小费用最大流,然后将每一种货物跑出来的最小花费加到一起,就是这道题目的答案。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
typedef long long LL;
const LL INF = 1e18;
const int N = 5005;
const int M = 50005;

LL n, m, k, en, ans = 0, s, t;
LL front[N], vis[N];
LL cst[55][55][55];

struct Edge {
    
    
	LL v, w, c, next;
}e[M * 4];

void addEdge(LL u, LL v, LL w, LL c) {
    
    
	++en;
	e[en].v = v;
	e[en].w = w;
	e[en].c = c;
	e[en].next = front[u]; 
	front[u] = en;
}

struct MCMF {
    
    
	LL dis[N], cur[N];
	LL node_cnt, maxflow, mincost;
	
	bool spfa(int _s, int _t) {
    
    
		for (int i = 1; i <= node_cnt; ++i) {
    
    
			dis[i] = INF;
			cur[i] = front[i];
		}
		queue<LL> q;
		q.push(_s); dis[_s] = 0; vis[_s] = 1;
		while (!q.empty()) {
    
    
			int u = q.front();
			q.pop();
			vis[u] = 0;
			for (LL i = front[u]; i; i = e[i].next) {
    
    
				LL v = e[i].v, w = e[i].w, c = e[i].c;
				if (w and dis[v] > dis[u] + c) {
    
    
					dis[v] = dis[u] + c;
					if (!vis[v]) {
    
    
						q.push(v); vis[v] = 1;
					}
				}
			}
		}
		return (dis[_t] != INF);
	}
	
	LL dfs(LL u, LL _t, LL flow) {
    
    
		if (u == _t) return flow;
		vis[u] = 1;
		LL ans = 0;
		for (LL &i = cur[u]; i and ans < flow; i = e[i].next) {
    
    
			LL v = e[i].v, w = e[i].w, c = e[i].c;
			if (!vis[v] and w and dis[v] == dis[u] + c) {
    
    
				LL x = dfs(v, _t, min(w, flow - ans));
				if (x) {
    
    
					mincost += x * c;
					e[i].w -= x;
					e[i ^ 1].w += x;
					ans += x;
				}
			}
		}
		vis[u] = 0;
		return ans;
	}
	
	void opt(int _s, int _t) {
    
    
		maxflow = mincost = 0;
		while (spfa(_s, _t)) {
    
    
			int x;
			while ((x = dfs(_s, _t, INF))) {
    
    
				maxflow += x;
			}
		}
	}
}fl; 

vector<LL> ord[55], ned[55];

void main2() {
    
    
	ans = 0;
	for (int i = 1; i <= n; ++i) {
    
    
		ned[i].clear();
		for (int j = 1; j <= k; ++j) {
    
    
			int x; cin >> x;
			ned[i].push_back(x);
		}
	}
	for (int i = 1; i <= m; ++i) {
    
    
		ord[i].clear();
		for (int j = 1; j <= k; ++j) {
    
    
			int x; cin >> x;
			ord[i].push_back(x);
		}
	}
	for (int x = 1; x <= k; ++x) {
    
    
		for (int i = 1; i <= n; ++i) {
    
    
			for (int j = 1; j <= m; ++j) {
    
    
				cin >> cst[x][j][i];
			}
		}
	}
	fl.node_cnt = n + m + 2;
	t = fl.node_cnt;
	s = t - 1;
	for (int K = 1; K <= k; ++K) {
    
    
		en = 1;
		for (int i = 1; i <= fl.node_cnt; ++i) {
    
    
			front[i] = 0;
		}
		for (int i = 1; i <= m; ++i) {
    
    
			if (ord[i][K - 1] == 0) continue;
			for (int j = 1; j <= n; ++j) {
    
    
				if (ned[j][K - 1] == 0) continue;
				for (int x = 1; x <= ord[i][K - 1]; ++x) {
    
    
					addEdge(i, j + m, 1ll, cst[K][i][j]);
					addEdge(j + m, i, 0ll, -cst[K][i][j]);
				}
			}
		}
		for (int i = 1; i <= m; ++i) {
    
    
			addEdge(s, i, ord[i][K - 1], 0ll);
			addEdge(i, s, 0ll, 0ll);
		}
		for (int i = 1; i <= n; ++i) {
    
    
			addEdge(i + m, t, ned[i][K - 1], 0ll);
			addEdge(t, i + m, 0ll, 0ll);
		}
		LL tar = 0;
		for (int i = 1; i <= n; ++i) {
    
    
			tar += ned[i][K - 1];
		}
		fl.opt(s, t);
		if (fl.maxflow < tar) {
    
    
			cout << -1 << '\n';
			return;
		}
		ans += fl.mincost;
	}
	cout << ans << '\n';
}

int main() {
    
    
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	while (1) {
    
    
		cin >> n >> m >> k;
		if (!n and !m and !k) break;
//		cout << "n = " << n << " m = " << m << " k = " << k << '\n';
		main2();
	}
	return 0;
}

3.最小割

HDU4289 - Control

题目链接

已知起点和终点,要求用最小的费用将图变为起点与终点不连通,这显然是求最小割。然而我们的代价在点上,这个时候我们没法使用网络流,需要将整张图变个样子。

我们采用拆点的方式,将第 i i i个点变为由 i i i号点和 i + n i+n i+n号点组成的点对,并从 i i i号点向 i + n i+n i+n号点连接一条边权为这个点的代价的有向边。
接下来所有的边 ( u , v ) (u,v) (u,v),我们都建立 u + n u+n u+n v v v之间的边。双向边的另一条边同理。其他的边权为 I N F INF INF

根据最大流=最小割,我们跑一个最大流,即可求出这道题目的答案。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL INF = 1e18;

LL front[100050], a[100050];
LL en = 1;

LL n, m, s, t;

struct Edge {
    
    
	LL v, w, next;
}e[100050];

void addEdge(LL u, LL v, LL w) {
    
    
	++en;
	e[en].v = v;
	e[en].w = w;
	e[en].next = front[u];
	front[u] = en;
}

struct ISAP {
    
    
	LL maxflow, node_cnt;
	LL dep[100050], gap[100050];
	
	void bfs() {
    
    
		for (LL i = 1; i <= node_cnt; ++i) {
    
    
			dep[i] = -1; gap[i] = 0;
		}
		dep[t] = 0; gap[0] = 1;
		queue<LL> q; q.push(t);
		while (!q.empty()) {
    
    
			int u = q.front();
			q.pop();
			for (LL i = front[u]; i; i = e[i].next) {
    
    
				LL v = e[i].v;
				if (dep[v] != -1) continue;
				q.push(v); dep[v] = dep[u] + 1;
				++gap[dep[v]];
			}
		}
		return;
	}
	
	LL dfs(LL u, LL flow) {
    
    
		if (u == t) {
    
    
			maxflow += flow; return flow;
		}
		LL used = 0;
		for (LL i = front[u]; i; i = e[i].next) {
    
    
			LL d = e[i].v;
			if (e[i].w and dep[d] + 1 == dep[u]) {
    
    
				LL mi = dfs(d, min(e[i].w, flow - used));
				if (mi) {
    
    
					e[i].w -= mi; e[i ^ 1ll].w += mi;
					used += mi;
				}
				if (used == flow) return used;
			}
		}
		--gap[dep[u]];
		if (gap[dep[u]] == 0) dep[s] = node_cnt + 1;
		++dep[u]; ++gap[dep[u]];
		return used;
	}
	
	void opt() {
    
    
		maxflow = 0;
		bfs();
		while (dep[s] < node_cnt) dfs(s, INF);
	}
}isap;

int ss, tt;

void main2() {
    
    
	cin >> m;
	isap.node_cnt = n * 2 + 2;
	s = n * 2 + 1; t = s + 1;
	en = 1;
	cin >> ss >> tt;
	for (int i = 1; i <= n; ++i) {
    
    
		cin >> a[i];
	}
	for (int i = 1; i <= n * 2 + 2; ++i) {
    
    
		front[i] = 0;
	}
	addEdge(s, ss, INF);
	addEdge(ss, s, 0);
	addEdge(tt + n, t, INF);
	addEdge(t, tt + n, 0);
	for (int i = 1; i <= n; ++i) {
    
    
		addEdge(i, i + n, a[i]);
		addEdge(i + n, i, 0);
	}
	for (int i = 1; i <= m; ++i) {
    
    
		int x, y;
		cin >> x >> y;
		addEdge(x + n, y, INF);
		addEdge(y, x + n, 0);
		addEdge(y + n, x, INF);
		addEdge(x, y + n, 0);
	}
	isap.opt();
	cout << isap.maxflow << '\n';
}

int main() {
    
    
//	freopen("Fin.in", "r", stdin);
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	LL _ = 1;
//	cin >> _;
	while (cin >> n) main2();
	return 0;
}

UVA10480 - Sabotage

题目链接

求最小割并输出方案。求最小割=求最大流,不多赘述。接下来考虑如何输出任意方案。

我们发现,最小割的结果是跑完最大流后的残量网络中起点和终点不连通。而在残量网络上我们可以通过dfs找到所有与起点相连的边。将这些点染色为黑色。剩下的跑不到的点染色为白色。接下来遍历所有的边,可以作为答案的一组边满足其两端的点所染的颜色不同。将这些边中不重复的、不互为正反边的边输出出来。这必然可以成为一种方案。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL INF = 1e18;

LL front[100050], vis[100005];
LL en = 1;

LL n, m, s, t;

struct Edge {
    
    
	LL u, v, w, next;
}e[100050];

void addEdge(LL u, LL v, LL w) {
    
    
	++en;
	e[en].u = u;
	e[en].v = v;
	e[en].w = w;
	e[en].next = front[u];
	front[u] = en;
}

struct ISAP {
    
    
	LL maxflow, node_cnt;
	LL dep[100050], gap[100050];
	
	void bfs() {
    
    
		for (LL i = 1; i <= node_cnt; ++i) {
    
    
			dep[i] = -1; gap[i] = 0;
		}
		dep[t] = 0; gap[0] = 1;
		queue<LL> q; q.push(t);
		while (!q.empty()) {
    
    
			int u = q.front();
			q.pop();
			for (LL i = front[u]; i; i = e[i].next) {
    
    
				LL v = e[i].v;
				if (dep[v] != -1) continue;
				q.push(v); dep[v] = dep[u] + 1;
				++gap[dep[v]];
			}
		}
		return;
	}
	
	LL dfs(LL u, LL flow) {
    
    
		if (u == t) {
    
    
			maxflow += flow; return flow;
		}
		LL used = 0;
		for (LL i = front[u]; i; i = e[i].next) {
    
    
			LL d = e[i].v;
			if (e[i].w and dep[d] + 1 == dep[u]) {
    
    
				LL mi = dfs(d, min(e[i].w, flow - used));
				if (mi) {
    
    
					e[i].w -= mi; e[i ^ 1ll].w += mi;
					used += mi;
				}
				if (used == flow) return used;
			}
		}
		--gap[dep[u]];
		if (gap[dep[u]] == 0) dep[s] = node_cnt + 1;
		++dep[u]; ++gap[dep[u]];
		return used;
	}
	
	void opt() {
    
    
		maxflow = 0;
		bfs();
		while (dep[s] < node_cnt) dfs(s, INF);
	}
}isap;

void dfs(int u) {
    
    
	vis[u] = 1;
	for (int i = front[u]; i; i = e[i].next) {
    
    
		int v = e[i].v, w = e[i].w;
		if (vis[v]) continue;
		if (w > 0) dfs(v);
	}
}

void main2() {
    
    
	isap.node_cnt = n + 2;
	s = n + 1;
	t = n + 2;
	for (int i = 1; i <= n + 2; ++i) {
    
    
		front[i] = 0;
	}
	en = 1;
	addEdge(s, 1, INF);
	addEdge(2, t, INF);
	for (int i = 1; i <= m; ++i) {
    
    
		int x, y, z;
		cin >> x >> y >> z;
		addEdge(x, y, z);
		addEdge(y, x, 0);
		addEdge(y, x, z);
		addEdge(x, y, 0);
	}
	isap.opt();
	for (int i = 1; i <= n + 2; ++i) {
    
    
		vis[i] = 0;
	}
	vector<pair<int, int> > ans;
	dfs(1);
	for (int i = 2; i <= en; i += 2) {
    
    
		int u = e[i].u, v = e[i].v;
		if (u > v or u > n or v > n) continue;
		if (vis[u] ^ vis[v]) {
    
    
			cout << u << ' ' << v << '\n';
		}
	}
	cout << '\n';
}

int main() {
    
    
//	freopen("Fin.in", "r", stdin);
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	while (1) {
    
    
		cin >> n >> m;
		if (!n and !m) break;
		main2();
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/xhyu61/article/details/126651713
今日推荐