poj 3436 ACM Computer Factory

来源:互联网 发布:python编写程序化交易 编辑:程序博客网 时间:2024/05/18 03:23

Problem

poj.org/problem?id=3436
vjudge.net/contest/68128#problem/A

Reference

POJ 3436 ACM Computer Factory(最大流)

Meaning

电脑由 p 个部件组成。有 n 台组装电脑的机器,每台机器有 3 种参数:

  1. Qi,表示最大性能,每小时装 Qi 台电脑;
  2. P 个数 Si,1,…,Si,p,表示输入电脑的组成,有 3 种情况:
    • 0:该部件不能有
    • 1:该部件必须有
    • 2:该部件可有可无
  3. 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;}
原创粉丝点击