题意:给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;
}