POJ 3436——ACM Computer Factory——最大流-拆点建图

 题意:

电脑公司生产电脑有N个机器(N条生产线),每个机器单位时间产量为Qi。 电脑由P个部件组成,每个机器工作时只能把有某些部件的半成品电脑(或什么都没有的空电脑)变成有另一些部件的半成品电脑或完整电脑(也可能移除某些部件)。求电脑公司的单位时间最大产量,以及哪些机器(生产线)有协作关系,即一台机器(一条生产线)把它的产品交给哪些机器(生产线)加工。


样例说明:

3     4

1号生产线:   15  0 0 0   -->   0 1 0

2号生产线:   10  0 0 0   -->   0 1 1

3号生产线:   30  0 1 2   -->   1 1 1

4号生产线:   3    0 2 1   -->   1 1 1

1号生产线能加工 0 0 0 这样的零件(这样的零件也就是无限制零件,源点),它可以把零件加工成 0 1 0 这个样子,然后 3 号生产线可以加工这种零件,并且加工成 1 1 1也就是成品,到这里也就加工成功了,因为1号生产线每次可以加工15个零件,所以 1->3 的加工量就是15,同理可得 2->3 的加工量是 10,所以结果是 25。


题解:

错误的网络流模型:

1) 添加一个源点S,S提供最初的原料 00000...
2) 添加一个汇点T, T接受最终的产品 11111...
3) 将每个机器视为一个节点编号为i, 源点S编号为 0,汇点T编号为 N+1。
4) S 连边到所有接收 "0000..." 或 "若干个0及若干个2" 的机器,容量为无穷大。
5) 若机器A产出的电脑能被机器B接受,则机器A连边到机器B,容量为机器A的产量。
6) 能产出成品 "1111...." 的机器,连边到T,容量无穷大。
7) 求S到T的最大流

初始残量图如下:

最终残量图如下:

若用该网络流模型,第一个样例最终的结果为35,而正确结果为25。从图中得出,2 这台机器超出了它的生产能力。故为错误的网络流模型。


正确的网络流模型:

1) 添加一个源点S,S提供最初的原料 00000...
2) 添加一个汇点T, T接受最终的产品 11111...
3) 将每个机器拆成两个点: 编号为i的接收节点,和编号为i+n的产出节点(n是机器数目),前者用于接收原料,后者用于提供加工后的半成品或成品。这两个点之间要连一条边,容量为单位时间产量Qi
4) S 连边到所有接收 "0000..." 或 "若干个0及若干个2" 的机器,容量为无穷大
5) 产出节点连边到能接受其产品的接收节点,容量无穷大
6) 能产出成品的节点,连边到T,容量无穷大。
7) 求S到T的最大流


AC代码如下:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<queue>
#include<stack>
#include<vector>
#include<string.h>
using namespace std;

#define runfile freopen("E:/Code/CB/Root/data.txt", "r", stdin)
#define stopfile fclose(stdin)
#define inf 100000000
#define ll long long
#define maxp 15

struct Node{
    int q;
    int s[maxp];
    int d[maxp];
};

struct node{
    int s;
    int d;
    int flow;
};

const int maxn = 55*2+1;
int g[maxn][maxn],g_copy[maxn][maxn],level[maxn],n,p;
Node ma[maxn];
node b[maxn];

void cre_graph()
{
    memset(g, 0 , sizeof(g));
    for(int i = 1; i <= n; i++)
    {
        scanf("%d",&ma[i].q);
        g[i][i+n] = ma[i].q;//将一台机器分为两个点,用一条边相连,容量为q
        int s1 = 0,d1 = 0;
        for(int j = 1; j <= p; j++)
        {
            scanf("%d",&ma[i].s[j]);
            if(ma[i].s[j] == 1)
                s1 = 1;
        }
        //添加一个源点,提供最初始的原材料0000...
        //添加一个汇点,接受最终的产品1111...
        if(!s1)//如果s[j]不含1,则将其与源点相连,容量为inf
            g[0][i] = inf;
        for(int k = 1; k <= p; k++)
        {
            scanf("%d",&ma[i].d[k]);
            if(ma[i].d[k] == 0)
                d1 = 1;
        }
        if(!d1)//如果d[j]不含0,则将其与汇点相连,容量为inf
            g[i+n][2*n+1] = inf;
    }
}

void connect()
{
    //将产出节点连边到能接受其产品的接收节点,容量无穷大
    int c;
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= n; j++)
        {
            c = 0;
            for(int k = 1; k <= p ;k++)
            {
                if((ma[i].d[k] == 0 && ma[j].s[k] == 1) || (ma[i].d[k] == 1 && ma[j].s[k] == 0))
                    break;
                else
                    c++;
            }
            if(c == p)
                g[i+n][j] = inf;
        }
    }
}

bool BFS()//用BFS来寻找增广路、构建分层图
{
    memset(level,-1,sizeof(level));//每次寻找增广路时,都将level数组初始化为-1
    level[0] = 0;//起点层次为0
    queue<int> q;
    q.push(0);
    while(!q.empty())
    {
        int cur = q.front();
        q.pop();
        for(int i = 0; i <= 2*n+1; i++)
        {
            if(g[cur][i] && level[i] == -1)//如果当前点与i顶点有边,并且i顶点没有被访问过
            {
                level[i] = level[cur] + 1;
                q.push(i);
            }
        }
    }
    if(level[2*n+1] == -1)//如果没有到达汇点,说明没有找到增广路
        return false;
    return true;
}

int DFS(int s, int MAX)//用DFS来构建残余网络、反向边,计算可行的最大流
{
    int a;
    if(s == 2*n+1)  return MAX;
    for(int i = 0; i <= 2*n+1; i++)
    {
        if(g[s][i] && level[i] == level[s]+1 && (a = DFS(i, min(MAX, g[s][i]))) )//取该路径中的最小残余流量
        {
            g[s][i] -= a;//构建残余网络
            g[i][s] += a;//构建反向边
            return a;
        }
    }
    return 0;//如果通过s点到达不了汇点,则返回0
}

ll Dinic()
{
    ll ans = 0;
    while(BFS())//每找到一条增广路
       ans += DFS(0,inf);//从源点出发,初始化最大可行流为无穷大
    //结果加上每条增广路得到的最大可行流
    return ans;
}

int path()
{
    //保存两两机器之间的信息
    int k = 0;
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= n; j++)
        {
            if(g_copy[i+n][j] > g[i+n][j])
            {
                b[k].s = i;
                b[k].d = j;
                b[k].flow = g_copy[i+n][j]-g[i+n][j];
                k++;
            }
        }
    }
    return k;
}

int main()
{
//    runfile;

    while(scanf("%d%d",&p,&n) != EOF)
    {
        cre_graph();
        connect();
        memcpy(g_copy, g, sizeof(g));
        ll maxflow = Dinic();
        int num = path();
        printf("%lld %d\n",maxflow,num);
        for(int i = 0; i < num; i++)
            printf("%d %d %d\n",b[i].s,b[i].d,b[i].flow);
    }

//    stopfile;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/cutedumpling/article/details/82017684