POJ 3436 ACM Computer Factory 网络流 最大流

题意:给n台机器,每个机器有输入输出规范,还有生产电脑的最大产能,输入规范中描述了每个部件是否是必需的,0表示不需要,1表示必需,2表示都行,输出规范中描述了机器输出的每个零件,0表示不输出该零件,1表示输出该零件。求最大总产能,以及需要连接的机器,及连接机器的产能。
思路:用最大流来求,难的主要是图的建模:每个机器当作一个点,由于每个点有最大容量,故可将其拆成两个点,把最大容量放到边上,若机器输入都是0,则将其连到源点,若输出都是1,将其连到汇点,若两个机器的输入输出相匹配,则在其之间连一条边,之后根据这样建的图来求最大流。需要注意的是连接源点时若输入为2也表示可以连接,WA多次主要是这个原因。
Ford-Fulkerson算法:每次通过dfs来求增广路,并沿其增广

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<vector>
using namespace std;
typedef long long ll;
const int maxn = 1005;
const int inf = 0x3f3f3f3f;
int p, n, vis[maxn];
struct edge {
	int to, cap, rev;
	edge(int to = 0, int cap = 0, int rev = 0) : to(to), cap(cap), rev(rev) {}
};
struct node {
	int c, in[15], out[15];
}com[maxn];
vector<edge> g[maxn];
vector<edge> res;
void addedge(int from, int to, int cap) 
{
	g[from].push_back(edge(to, cap, g[to].size()));
	g[to].push_back(edge(from, 0, g[from].size()-1));
}
int dfs(int v, int t, int f)
{
	if (v == t) return f;
	vis[v] = 1;
	for (int i = 0; i < g[v].size(); i++) {
		edge &e = g[v][i];
		if (!vis[e.to] && e.cap > 0) {
			int d = dfs(e.to, t, min(f, e.cap));
			if (d > 0) {
				e.cap -= d;
				g[e.to][e.rev].cap += d;
				return d;
			}
		}
	}
	return 0;
}
int solve(int s, int t)
{
	int flow = 0;
	for (;;) {
		memset(vis, 0, sizeof(vis));
		int f = dfs(s, t, inf);
		if (f == 0) return flow;
		flow += f;
	}
}
int main()
{
	while (cin >> p >> n) {
		res.clear();
		for (int i = 0; i <= 2*n+1; i++) {
			g[i].clear();
		}
		for (int i = 1; i <= n; i++) {
			cin >> com[i].c;
			for (int j = 1; j <= p; j++)
				cin >> com[i].in[j];
			for (int j = 1; j <= p; j++)
				cin >> com[i].out[j];
		}
		int N = 2*n+1;
		for (int i = 1; i <= n; i++)
			addedge(2*i-1, 2*i, com[i].c);
		for (int i = 1; i <= n; i++) {
			int ok = 0;
			for (int j = 1; j <= p; j++) {
				if (com[i].in[j] != 0 && com[i].in[j] != 2) {
					ok = 1;
					break;
				}
			}
			if (!ok)
				addedge(0, 2*i-1, inf);
			ok = 0;
			for (int j = 1; j <= p; j++) {
				if (com[i].out[j] != 1) {
					ok = 1;
					break;
				}
			}
			if (!ok)
				addedge(2*i, N, inf);
			for (int j = 1; j <= n; j++) {
				ok = 0;
				for (int k = 1; k <= p; k++) {
					if (j != i && !(com[i].out[k] == com[j].in[k] || com[j].in[k] == 2)){
						ok = 1;
						break;
					} 
				}
				if (!ok) {
					addedge(2*i, 2*j-1, inf);
				}
			}
		}
		vector<edge> temp[maxn];
		for (int i = 0; i <= N; i++) {
			temp[i] = vector<edge>(g[i]);
		}
		printf("%d ", solve(0, N));
		for (int i = 1; i <= n; i++) {
			for (int j = 0; j < g[2*i].size(); j++) {
				if (g[2*i][j].cap < temp[2*i][j].cap && g[2*i][j].to != N) 
					res.push_back(edge(i, temp[2*i][j].cap-g[2*i][j].cap, g[2*i][j].to/2+1));
			}
		}
		printf("%d\n", res.size());
		for (int i = 0; i < res.size(); i++)
			printf("%d %d %d\n", res[i].to, res[i].rev, res[i].cap);
	}	
	return 0;
}

Edmonds-Karp算法:FF算法的特例,用bfs来求增广路,每次找的都是最短的增广路,最短增广路的长度在增广过程中不会变短。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn = 105;
const int inf = 0x3f3f3f3f;
int p, n, vis[maxn], g[maxn][maxn], path[maxn], flow[maxn], N, temp[maxn][maxn];
struct edge {
	int to, cap, rev;
	edge(int to = 0, int cap = 0, int rev = 0) : to(to), cap(cap), rev(rev) {}
};
struct node {
	int c, in[15], out[15];
}com[maxn];
vector<edge> res;
int bfs()
{
	queue<int> q;
	memset(path, -1, sizeof(path));
	path[0] = 0;
	flow[0] = inf;
	q.push(0);
	while (!q.empty()) {
		int t = q.front(); q.pop();
		if (t == N) break;
		for (int i = 0; i <= N; i++) {
			if (i != 0 && path[i] == -1 && g[t][i]) {
				flow[i] = flow[t] < g[t][i] ? flow[t] : g[t][i];
				q.push(i);
				path[i] = t;
			}
		}
	}
	if (path[N] == -1) return -1;
	return flow[N];
}
int solve()
{
	int maxflow = 0, step, now, pre;
	while ((step = bfs()) != -1) {
		maxflow += step;
		now = N;
		while (now != 0) {
			pre = path[now];
			g[pre][now] -= step;
			g[now][pre] += step;
			now = pre;
		}
	}
	return maxflow;
}
int main()
{
	while (cin >> p >> n) {
		res.clear();
		memset(g, 0, sizeof(g));
		for (int i = 1; i <= n; i++) {
			cin >> com[i].c;
			for (int j = 1; j <= p; j++)
				cin >> com[i].in[j];
			for (int j = 1; j <= p; j++)
				cin >> com[i].out[j];
		}
		N = 2*n+1;
		for (int i = 1; i <= n; i++)
			g[2*i-1][2*i] = com[i].c;
		for (int i = 1; i <= n; i++) {
			int ok = 0;
			for (int j = 1; j <= p; j++) {
				if (com[i].in[j] != 0 && com[i].in[j] != 2) {
					ok = 1;
					break;
				}
			}
			if (!ok)
				g[0][2*i-1] = inf;
			ok = 0;
			for (int j = 1; j <= p; j++) {
				if (com[i].out[j] != 1) {
					ok = 1;
					break;
				}
			}
			if (!ok)
				g[2*i][N] = inf;
			for (int j = 1; j <= n; j++) {
				if (i == j)
					continue;
				ok = 0;
				for (int k = 1; k <= p; k++) {
					if (!(com[i].out[k] == com[j].in[k] || com[j].in[k] == 2)){
						ok = 1;
						break;
					} 
				}
				if (!ok) {
					g[2*i][2*j-1] = inf;
				}
			}
		}
		memcpy(temp, g, sizeof(g));
		printf("%d ", solve());
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= n; j++) {
				if (g[2*i][2*j-1] < temp[2*i][2*j-1]) 
					res.push_back(edge(i, temp[2*i][2*j-1]-g[2*i][2*j-1], j));
			}
		}
		printf("%d\n", res.size());
		for (int i = 0; i < res.size(); i++)
			printf("%d %d %d\n", res[i].to, res[i].rev, res[i].cap);
	}	
	return 0;
}

实际上这个实现比第一个实现要慢,因为第一个实现中根据每个点的边来遍历,而第二个实现中遍历所有点来查找两点之间的边
Dinic算法:EK算法的优化版本,复杂度在三个里面是最优的

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn = 105;
const int inf = 0x3f3f3f3f;
int p, n, vis[maxn], level[maxn], iter[maxn], N;
struct edge {
	int to, cap, rev;
	edge(int to = 0, int cap = 0, int rev = 0) : to(to), cap(cap), rev(rev) {}
};
struct node {
	int c, in[15], out[15];
}com[maxn];
vector<edge> g[maxn];
vector<edge> res;
void addedge(int from, int to, int cap) 
{
	g[from].push_back(edge(to, cap, g[to].size()));
	g[to].push_back(edge(from, 0, g[from].size()-1));
}
void bfs(int s)
{
	memset(level, -1, sizeof(level));
	queue<int> q;
	level[s] = 0;
	q.push(s);
	while (!q.empty()) {
		int t = q.front(); q.pop();
		for (int i = 0; i < g[t].size(); i++) {
			edge &e = g[t][i];
			if (e.cap > 0 && level[e.to] < 0) {
				level[e.to] = level[t] + 1;
				q.push(e.to);
			}
		}
	}
}
int dfs(int v, int t, int f)
{
	if (v == t) return f;
	vis[v] = 1;
	for (int &i = iter[v]; i < g[v].size(); i++) {
		edge &e = g[v][i];
		if (level[v] < level[e.to] && e.cap > 0) {
			int d = dfs(e.to, t, min(f, e.cap));
			if (d > 0) {
				e.cap -= d;
				g[e.to][e.rev].cap += d;
				return d;
			}
		}
	}
	return 0;
}
int solve(int s, int t)
{
	int flow = 0;
	for (;;) {
		bfs(s);
		if (level[t] < 0) return flow;
		memset(iter, 0, sizeof(iter));
		int f;
		while ((f = dfs(s, t, inf)) > 0)
			flow += f;
	}
}
int main()
{
	while (cin >> p >> n) {
		res.clear();
		memset(g, 0, sizeof(g));
		for (int i = 1; i <= n; i++) {
			cin >> com[i].c;
			for (int j = 1; j <= p; j++)
				cin >> com[i].in[j];
			for (int j = 1; j <= p; j++)
				cin >> com[i].out[j];
		}
		N = 2*n+1;
		for (int i = 1; i <= n; i++)
			addedge(2*i-1, 2*i, com[i].c);
		for (int i = 1; i <= n; i++) {
			int ok = 0;
			for (int j = 1; j <= p; j++) {
				if (com[i].in[j] != 0 && com[i].in[j] != 2) {
					ok = 1;
					break;
				}
			}
			if (!ok)
				addedge(0, 2*i-1, inf);
			ok = 0;
			for (int j = 1; j <= p; j++) {
				if (com[i].out[j] != 1) {
					ok = 1;
					break;
				}
			}
			if (!ok)
				addedge(2*i, N, inf);
			for (int j = 1; j <= n; j++) {
				if (i == j)
					continue;
				ok = 0;
				for (int k = 1; k <= p; k++) {
					if (!(com[i].out[k] == com[j].in[k] || com[j].in[k] == 2)){
						ok = 1;
						break;
					} 
				}
				if (!ok) {
					addedge(2*i, 2*j-1, inf);
				}
			}
		}
		vector<edge> temp[maxn];
		for (int i = 0; i <= N; i++) {
			temp[i] = vector<edge>(g[i]);
		}
		printf("%d ", solve(0, N));
		for (int i = 1; i <= n; i++) {
			for (int j = 0; j < g[2*i].size(); j++) {
				if (g[2*i][j].cap < temp[2*i][j].cap && g[2*i][j].to != N) 
					res.push_back(edge(i, temp[2*i][j].cap-g[2*i][j].cap, g[2*i][j].to/2+1));
			}
		}
		printf("%d\n", res.size());
		for (int i = 0; i < res.size(); i++)
			printf("%d %d %d\n", res[i].to, res[i].rev, res[i].cap);
	}	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值