Problem
poj.org/problem?id=3436
vjudge.net/contest/68128#problem/A
Reference
POJ 3436 ACM Computer Factory(最大流)
Meaning
电脑由 p 个部件组成。有 n 台组装电脑的机器,每台机器有 3 种参数:
- Qi ,表示最大性能,每小时装 Qi 台电脑;
- P 个数
Si,1
,…,
Si,p
,表示输入电脑的组成,有 3 种情况:
- 0:该部件不能有
- 1:该部件必须有
- 2:该部件可有可无
- P 个数
Di,1
,…,
Di,p
,表示组装完后电脑的组成,有 2 种情况:
- 0:该部件不存在
- 1:该部件存在
输入中没有 1 的就可以是原始输入,输出中全是 1 的就是最终输出。
现在要在这些机器间加边,使得总的生产效率最大。
Analysis
可能有多个可接受原始输入的机器,所以加一个超级源点,向这些点连边;同理,加一个超级汇点,让所有可以有最终输出的机器连向它。
机器的有流量(效率)限制,由于限制在点上,所以把点拆成两个点:输入点和输出点,两个点之间连一条边,容量就是该机器的效率。
建好图后要备份一下图,跑完最大流后两个图的边比较得知哪些边是必须要加的。
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 50, P = 10, Q = 10000;
int g[N+1<<1][N+1<<1]; // 邻接矩阵存图
int aug[N+1<<1], pre[N+1<<1];
int Edmonds_Karp(int s, int t)
{
int flow = 0;
while(1)
{
memset(aug, 0, sizeof aug);
aug[s] = Q;
queue<int> que;
que.push(s);
for(int tp; !que.empty(); que.pop())
{
tp = que.front();
for(int i = 1; i <= t; ++i)
if(!aug[i] && g[tp][i] > 0)
{
pre[i] = tp;
aug[i] = min(aug[tp], g[tp][i]);
que.push(i);
}
if(aug[t])
break;
}
if(!aug[t])
break;
for(int v = t; v != s; v = pre[v])
{
g[pre[v]][v] -= aug[t];
g[v][pre[v]] += aug[t];
}
flow += aug[t];
}
return flow;
}
int q[N+1], s[N+1][P], d[N+1][P];
int G[N+1<<1][N+1<<1]; // 图 g 的备份
int from[N*N], to[N*N], cap[N*N]; // 必须建的边
int main()
{
int p, n;
scanf("%d%d", &p, &n);
for(int i = 1; i <= n; ++i)
{
scanf("%d", q+i);
for(int j = 0; j < p; ++j)
scanf("%d", s[i]+j);
for(int j = 0; j < p; ++j)
scanf("%d", d[i]+j);
}
memset(g, 0, sizeof g);
// 拆点 -> i 和 i+n 对应
// i 是输入,i + n 是输出
for(int i = 1; i <= n; ++i)
g[i][i+n] = q[i];
// 点间、与源点、与汇点连边
for(int i = 1; i <= n; ++i)
{
// 与源点、汇点
bool st = true, tm = true;
for(int j = 0; j < p; ++j)
{
// 输入无 1 连源点
st &= s[i][j] != 1;
// 输出全 1 连汇点
tm &= d[i][j] == 1;
}
if(st) // 源点到输入点
g[0][i] = Q; // 0 是源点
if(tm) // 输出点到汇点
g[i+n][n<<1|1] = Q; // 2n+1 是汇点
// 点间
for(int j = 1; j <= n; ++j)
if(i != j)
{
int f = Q;
for(int k = 0; k < p; ++k)
if(d[i][k] + s[j][k] == 1) // 一个是 0 另一个是 1
{
f = 0; // 则不能连边
break;
}
g[i+n][j] = f;
}
}
memcpy(G, g, sizeof g); // 备份原图
printf("%d ", Edmonds_Karp(0, n<<1|1));
int cnt = 0;
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j)
if(g[i+n][j] < G[i+n][j]) // 上限变小 -> 此路有被使用过
{
from[cnt] = i;
to[cnt] = j;
cap[cnt++] = G[i+n][j] - g[i+n][j];
}
printf("%d\n", cnt);
while(cnt--)
printf("%d %d %d\n", from[cnt], to[cnt], cap[cnt]);
return 0;
}