POJ 3436 网络流+拆分

题意

N台机器生产P个产品,流水线作业。每台机器可以同时执行W个相同任务,每个任务将P个产品的状态从P个状态变换为另外P个状态。求最多可以同时生产多少个产品?

题解

WA了好多次。。。不过讲道理这道题的核心在于点拆分,至于Dinic算法,直接把《训练指南》那本书上的Dinic代码搞上去就可以了。一旦涉及到这种流水线作业,肯定是要有一个超级源点和超级终点,这个在《挑战程序设计竞赛》上讲的很详细,这里就不解释了。至于点拆分,很巧妙,将点与其他点的流限制设为INF,将点的起点和终点拆分,流限制为W。然后再套用Dinic,很轻松就能求出来最大流。

注意事项

没什么好注意的,最重要的是Dinic算法一定要敲对,在这上面WA了好几发。。

代码

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<iostream>
#include<queue>
#define MAXN 120
#define INF 1e9
using namespace std;

int p,n;
bool isStart(int x[]){
    for(int i=0;i<p;i++){
        if(x[i]==1)
            return false;
    }
    return true;
}
bool isEnd(int x[]){
    for(int i=0;i<p;i++){
        if(x[i]==0)
            return false;
    }
    return true;
}

bool isInAndOut(int in[],int out[]){
    for(int i=0;i<p;i++){
        if(out[i]!=in[i]&&in[i]!=2){
            return false;
        }
    }
    return true;
}

struct Edge{
    int from,to,cap,flow;
};


struct Dinic{
    int n,m,s,t;
    vector<Edge> edges;
    vector<int> g[MAXN];
    bool vis[MAXN];
    int d[MAXN];
    int cur[MAXN];

    void init(int n){
        this->n=n;
        edges.clear();
        for(int i=0;i<=n;i++)
            g[i].clear();
    }

    void addEdge(int from,int to,int cap){
        edges.push_back((Edge){from,to,cap,0});
        edges.push_back((Edge){to,from,0,0});
        m=edges.size();
        g[from].push_back(m-2);
        g[to].push_back(m-1);
    }

    bool bfs(){
        memset(vis,0,sizeof(vis));
        memset(d,-1,sizeof(d));
        queue<int> q;
        q.push(s);
        d[s]=0;
        vis[s]=true;
        while(!q.empty()){
            int x=q.front();
            q.pop();
            for(int i=0;i<g[x].size();i++){
                Edge e=edges[g[x][i]];
                if(!vis[e.to]&&e.cap>e.flow){
                    vis[e.to]=true;
                    d[e.to]=d[x]+1;
                    q.push(e.to);
                }
            }
        }
        return vis[t];
    }


    int dfs(int x,int a){
        if(x==t||a==0)
            return a;
        int flow=0,f;
        for(int& i=cur[x];i<g[x].size();i++){
            Edge& e=edges[g[x][i]];
            if(d[x]+1==d[e.to]&&(f=dfs(e.to,min(a,e.cap-e.flow)))>0){
                e.flow+=f;
                edges[g[x][i]^1].flow-=f;
                flow+=f;
                a-=f;
                if(a==0)
                    break;

            }
        }
        return flow;
    }

    int maxFlow(int s,int t){
        this->s=s,this->t=t;
        int flow=0;
        while(bfs()){
            memset(cur,0,sizeof(cur));
            flow+=dfs(s,INF);
        }
        return flow;
    }
};

int main()
{
    int w[60],in[60][12],out[60][12];
    while(~scanf("%d%d",&p,&n)){
        Dinic di;
        int s=0,t=2*n+1;
        di.init(t);
        for(int i=1;i<=n;i++){
            scanf("%d",&w[i]);
            for(int j=0;j<p;j++){
                scanf("%d",&in[i][j]);
            }
            for(int j=0;j<p;j++){
                scanf("%d",&out[i][j]);
            }
            if(isStart(in[i])){
                di.addEdge(s,i,INF);
            }
            if(isEnd(out[i])){
                di.addEdge(i+n,t,INF);
            }
        }

        for(int i=1;i<=n;i++){
            di.addEdge(i,i+n,w[i]);
            for(int j=1;j<=n;j++){
                if(i==j)
                    continue;
                if(isInAndOut(in[j],out[i])){
                    di.addEdge(i+n,j,INF);
                }
            }
        }

        int flow=di.maxFlow(s,t);

        int cnt=0;
        for(int i=0;i<di.edges.size();i++){
            if(di.edges[i].from==s||di.edges[i].to==s||di.edges[i].from==t||di.edges[i].to==t)
                continue;
            if((di.edges[i].from+n)==di.edges[i].to||(di.edges[i].from-n)==di.edges[i].to)
                continue;
            if(di.edges[i].flow<0)
                cnt++;
        }
        printf("%d %d\n",flow,cnt);

        for(int i=0;i<di.edges.size();i++){
            if(di.edges[i].from==0||di.edges[i].to==0||di.edges[i].from==2*n+1||di.edges[i].to==2*n+1)
                continue;
            if((di.edges[i].from+n)==di.edges[i].to||(di.edges[i].from-n)==di.edges[i].to)
                continue;
            if(di.edges[i].flow<0)
                printf("%d %d %d\n",di.edges[i].to-n,di.edges[i].from,-di.edges[i].flow);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值