POJ-ACM Computer Factory(EK算法模板)

题目大意:

有n台机器,P种零件,每种零件有 0  和  1  两种状态,接下来有n行,每行第一个表示机器的效率,其余表示每台机器对于P零件的需求 0 表示不需要,1表示需要,2表示无所谓。现在需要找一些生产线可以把全部的零件制造出来(使零件的状态为1,起始状态为0)。求这些的生产线的最大生产量,并记录有几条生产线和每条生产线的路径

作为一道比较简单的网络流的题目!

这道题的主要问题为:

1.如何建图?如何判断机器可以连接?

2.如何寻找生产线?

3.如何记录生产线的条数和生产线的路径?

思路:

1.为了建图,可以开一个数组,两两判断,如果这两条边可以连接,那么在数组中记录一下两点的最小产量,简单来说就是流量

2.寻找生产线就用EK算法,相对来说比较简单,注意!这道题没有源点,或者说每个点都可能成为源点,为了方便,自己造一个源点和终点,源点初始化为0,终点初始化为1

EK算法详解

3.记录生产线

首先!如果加入新的路线 1->1 其实是不影响生产线的,但是如果把这条生产线存到数组中,在EK算法中使用的话,那么可以记录那些点流入过流量,或者流出过流量。(注意用额外的数组,不能跟原来的数组用一个数组)

这里的flow数组糅合了很多作用,所以开的比较大,是原来的4倍,这些部分都有用途


#include<stdio.h>
#include<queue>
#include<string.h>
#include<math.h>
#include<iostream>
using namespace std;
const int inf=0x3f3f3f3f;
const int MAXN=60;
const int MAXP=12;
int in[MAXN][2*MAXP],flow[2*MAXN][2*MAXN];
int n,p;
int EK(int s,int t)  //广搜搜索增广路
{
    queue<int> q;
    int p[MAXN*2],a[MAXN*2];
    int f=0;
    while(1)
    {
        memset(a,0,sizeof(a));
        a[s]=inf;
        q.push(s);
        while(!q.empty())
        {
            int u=q.front();
            q.pop();
            for(int v=0; v<=2*n+1; ++v)
                if(!a[v]&&flow[u][v])//如果这个点与u点相连并且未被选中过
                {
                    p[v]=u,q.push(v);//将V点加入队列中,p是用来记录路径
                    a[v]=min(a[u],flow[u][v]);//a是用来寻找增广路的
                    if(v==t) break;
                }
        }
        if(a[t]==0) break;//找不到增广路后跳出循环
        for(int u=t; u!=s; u=p[u])//增广路后,将所有的路减去相应流量!然后反向边加上流量
        {
            flow[p[u]][u]-=a[t];
            flow[u][p[u]]+=a[t];
        }
        f+=a[t];
    }
    return f;
}
int main()
{
    while(scanf("%d%d",&p,&n)==2)
    {
        for(int i=1; i<=n; ++i)
            for(int j=0; j<2*p+1; ++j)//将零件状态存入
                scanf("%d",&in[i][j]);
        for(int i=1; i<=2*p; ++i)//没有源点和终点,自己设立一个
        {
            in[0][i]=0;//第一行零件设为0,为源点
            in[n+1][i]=1;//n+1行零件全设为1,为终点
        }
        memset(flow,0,sizeof(flow));
        for(int i=1; i<=n; ++i)//每个点都连接到自己,然后流量是自己
            flow[i][i+n]=in[i][0];
        for(int i=0; i<=n+1; ++i)
            for(int j=0; j<=n+1; ++j)
            {
                if(i==j) continue;
                bool flag=true;
                for(int k=1; k<=p; ++k)
                {
                    if(in[j][k]!=2&&in[i][k+p]!=in[j][k])
                    {
                        flag=false;
                        break;
                    }
                }
                if(flag&&i==0) flow[i][j]=in[j][i];//和源点和终点相连的点特殊处理一下
                else if(flag&&j==n+1) flow[i+n][2*n+1]=in[i][0];
                else if(flag) flow[i+n][j]=min(in[i][0],in[j][0]);//将所有可以连接的机器记录一下,并记录这条边的最大流量
            }
        for(int i=0; i<=2*n+1; i++)
        {
            for(int j=0; j<=2*n+1; j++)
            {
                printf("%2d ",flow[i][j]);
            }
            printf("\n");
        }
        printf("\n\n");
        printf("%d ",EK(0,2*n+1));
        printf("\n\n\n");
        for(int i=0; i<=2*n+1; i++)
        {
            for(int j=0; j<=2*n+1; j++)
            {
                printf("%2d ",flow[i][j]);
            }
            printf("\n");
        }
        int cnt=0;
        for(int i=1; i<=n; ++i)
            for(int j=1; j<=n; ++j)
                if(flow[j][i+n]>0&&i!=j)
                {
                    printf("%d %d*-*\n",j,i+n);
                    ++cnt;
                }
        printf("%d\n",cnt);
        for(int i=1; i<=n; ++i)
            for(int j=1; j<=n; ++j)
                if(flow[j][i+n]>0&&i!=j)
                    printf("%d %d %d\n",i,j,flow[j][i+n]);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值