POJ 3436 ACM Computer Factory

ACM Computer Factory

原题大意:

为了追求ACM比赛的公平性,所有用作ACM比赛的电脑性能是一样的,而ACM董事会专门有一条生产线来生产这样的电脑,随着比赛规模的越来越大,生产线的生产能力不能满足需要,所以说ACM董事会想要重新建造一条生产线。

生产线是全自动化的,所以需要机器来组成生产线,给定有多少中种机器,标准ACM用电脑有多少部份,每种机器将什么样的ACM电脑半成品处理成什么样的电脑半成品(对于输入的电脑半成品,每部分有0,1,2三种状态:代表着 0、这部分必须没有我才能处理,1、这部分必须有我才能处理,2、这部分有没有我都能处理。对于输出的电脑半成品有0,1两种状态:代表着0,处理完后的电脑半成品里没有这部分,1、处理完的电脑半成品有这部分),每一个机器每小时可以处理Q个半成品(输入数据中的Qi)。

求电脑公司的单位时间最大产量,以及哪些机器有协作关系,即一台机器把它的产品交给哪些机器加工。(每小时最多生成多少成品,成品的定义就是所有部分的状态都是“1”)

Sample input 1
3 4
15  0 0 0  0 1 0
10  0 0 0  0 1 1
30  0 1 2  1 1 1
3   0 2 1  1 1 1
Sample output 1
25 2
1 3 15
2 3 10

第一行输入两个数:一个P代表有P个零件, 一个 N代表有N台机器。

接下来N行,每行第一个数字有Qi,代表 第i个零件每小时能加工的半成品零件个数。然后是2*P个数字,前P个数字是加工前半成品需要满足的条件,后P个数表示加工后的半成品的数量。

将原题抽象为图的步骤:

(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的最大流

Trick: S 可以连边到222, 020, ….

#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
const int INF=0x3f3f3f3f,maxn=2000;
struct node{
    int in[12],out[12];//第i台机器的输入输出规格
    int flow;//第i台机器能放出的最大流量
}P[maxn];
int G[maxn][maxn],backupG[maxn][maxn],visited[maxn],Layer[maxn];
int n,m;//n台机器,每台机器需要m个零件
bool OK(int a,int b){
    for(int i=0;i<m;i++){
        if(!(P[a].out[i]==P[b].in[i]||P[b].in[i]==2))
            return false;
    }
    return true;
}
bool CountLayer(int n){//分层
    queue<int>Q;
    memset(Layer,0xff,sizeof(Layer));//都初始化成-1
    Layer[0]=0;
    Q.push(0);
    while(!Q.empty()){
        int v=Q.front();
        Q.pop();
        for(int i=0;i<=n;i++){
            if(G[v][i]>0&&Layer[i]==-1){
                //Layer[i]==-1说明还没有访问过
                Layer[i]=Layer[v]+1;
                if(i==n) return true;//分层到汇点即可
                else Q.push(i);
            }
        }
    }
    return false;
}
int Dinic(int n){
    int maxFlow=0;
    deque<int>Q;//DFS用的栈
    while(CountLayer(n)){//只要能分层
        Q.push_back(0);//源点入栈
        memset(visited,0,sizeof(visited));
        visited[0]=1;
        while(!Q.empty()){
            int nd=Q.back();
            if(nd==n){//nd是汇点
                //在栈中寻找容量最小的边
                int minC=INF;
                int minC_vs;//容量最小的边的起点
                for(int i=1;i<Q.size();i++){
                    int vs=Q[i-1];
                    int ve=Q[i];
                    if(G[vs][ve]>0){
                        if(minC>G[vs][ve]){
                            minC=G[vs][ve];
                            minC_vs=vs;
                        }
                    }
                }
                //增广,改图
                maxFlow+=minC;
                for(int i=1;i<Q.size();i++){
                    int vs=Q[i-1];
                    int ve=Q[i];
                    G[vs][ve]-=minC;//修改边容量
                    G[ve][vs]+=minC;//添加反向边
                }
                //退栈到minC_vs成为栈顶,以便继续dfs
                while(!Q.empty()&&Q.back()!=minC_vs){
                    visited[Q.back()]=0;
                    Q.pop_back();
                }
            }
            else{//nd不是汇点
                int i;
                for(i=0;i<=n;i++){
                    if(G[nd][i]>0&&Layer[i]==Layer[nd]+1&&!visited[i]){
                        visited[i]=1;
                        Q.push_back(i);
                        break;
                    }
                }
                if(i>n) Q.pop_back();
            }
        }
    }
    return maxFlow;
}
int main(){
    //freopen("1.txt","r",stdin);
    while(scanf("%d %d",&m,&n)!=EOF){
        memset(G,0,sizeof(G));
        memset(P,0,sizeof(P));
        for(int i=1;i<=n;i++){
            scanf("%d",&P[i].flow);
            for(int j=0;j<m;j++)
                scanf("%d",&P[i].in[j]);
            for(int j=0;j<m;j++)
                scanf("%d",&P[i].out[j]);
        }
        for(int i=0;i<m;i++){
            P[0].in[i]=P[0].out[i]=0;
            P[n+1].in[i]=P[n+1].out[i]=1;
        }
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                if(i==j) G[i][i+n]=P[i].flow;
                else if(OK(i,j)) G[i+n][j]=INF;
            }
        }
        for(int i=1;i<=n;i++){
            if(OK(0,i)) G[0][i]=INF;
        }
        for(int i=1;i<=n;i++){
            if(OK(i,n+1)) G[n+i][2*n+1]=INF;
        }

        memcpy(backupG,G,sizeof(G));
        int maxFlow=Dinic(2*n+1);
//        for(int i=1;i<=2*n;i++){
//            for(int j=1;j<=2*n;j++){
//                printf("(%4d,%4d)",backupG[i][j],G[i][j]);
//            }
//            printf("\n");
//        }
        int num=0,a[maxn],b[maxn],c[maxn];
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                if(i==j) continue;
                if(backupG[i+n][j]>G[i+n][j]){
                    a[num]=i,b[num]=j,c[num]=backupG[i+n][j]-G[i+n][j];
                    num++;
                }
            }
        }
        printf("%d %d\n",maxFlow,num);
        for(int i=0; i<num; i++)
            printf("%d %d %d\n",a[i],b[i],c[i]);
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值