zoukankan      html  css  js  c++  java
  • poj 3436 最大流-拆点

    题意:

    有一些机器用来构成一个组装电脑的生产线,每台机器对输入机器的电脑有要求,符合要求的电脑被送入机器后会输出一台规定配件情况的电脑。而且分别告知每台机器在单位时间内处理电脑的台数。将这些机器连成一个生产线,使得单位时间内出产的完整的电脑数量最多,完整的电脑就是具有所有配件的电脑。输出单位时间内的最大出产台数。

    分析:

    这个是一个网络流,对流过每个点的流量有限制,这样就需要拆点,把每个结点拆成两个,一个入点,一个出点,并从入点到出点连接一条边流量为点的的流向限制,把所有接入该点的边接入它的入点,从该点流出的边从出点流出。

    这题的建图方法是,每个机器是一个点,把源与所有没有必须元件的入点连接,所有完整元件的出点与汇连接,若一台机器的输出能符合另一台机器的输入条件则连一条边。把每个机器拆点,其内部边流量为其生产速度,具体建图参考代码。

    因为点比较多,而符合要求的边是比较少的,所以求最大流采用的是EK算法,时间复杂度O(nm^2),可以尝试一下,如果用Dinic算法会慢点,时间复杂度是O(n^2m)


    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<vector>
    #include<queue>
    using namespace std;
    const int INF=0x3f3f3f3f;
    const int N=100+9;
    struct Edge
    {
        int from,to,cap,flow;
    };
    vector<Edge>edges;
    vector<int>G[N];
    int pre[N];//记录路径
    int vis[N];//起点到i的可改进量
    int s,t;
    bool bfs()
    {
        memset(vis,0,sizeof(vis));
        queue<int>q;
        q.push(s);
        vis[s]=INF;
        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]=min(vis[x],e.cap-e.flow);
                    pre[e.to]=G[x][i];
                    q.push(e.to);
                }
            }
        }
        return vis[t];
    }
    
    int Maxflow()
    {
        int flow=0;
        while(bfs()){
            for(int u=t;u!=s;u=edges[pre[u]].from){
                edges[pre[u]].flow+=vis[t];
                edges[pre[u]^1].flow-=vis[t];
            }
            flow+=vis[t];
        }
        return flow;
    }
    void addedge(int from,int to,int cap)
    {
        edges.push_back((Edge){from,to,cap,0});
        edges.push_back((Edge){to,from,0,0});
        int m=edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }
    int a[N][N],n,p;
    void build()
    {
        //建图拆点,其中1-n是出点,n+1-2n是入点
        for(int i=1;i<=n;i++){
            addedge(i,n+i,a[i][0]); //出点指向入点,容量是生产量
            bool flag=1;
            for(int j=1;j<=p;j++)if(a[i][j]==1)flag=0;
            if(flag)addedge(s,i,INF); //超源点指向入点,容量无限
            flag=1;
            for(int j=p+1;j<2*p+1;j++)if(a[i][j]!=1)flag=0;
            if(flag)addedge(n+i,t,INF); //出点指向超汇点,容量无限
            for(int j=1;j<=n;j++){
                if(i==j)continue;
                flag=1;
                for(int k=1;k<=p;k++)if(a[j][k]!=2&&a[j][k]!=a[i][k+p])flag=0;
                if(flag)addedge(n+i,j,INF); //如果某个出点可以和某个入点匹配,那么连边,容量无限
            }
        }
    
    }
    int main()
    {
        scanf("%d%d",&p,&n);
        for(int i=1;i<=n;i++)
            for(int j=0;j<2*p+1;j++)scanf("%d",&a[i][j]);
        s=0,t=2*n+1;
        build();
        int res=Maxflow();
        int tot=0;
        int ans[N][3];
        for(int u=n+1;u<t;u++)for(int i=0;i<G[u].size();i++){
            Edge& e=edges[G[u][i]];
            if(e.to>0&&e.to<=n&&e.flow>0)ans[tot][0]=u-n,ans[tot][1]=e.to,ans[tot++][2]=e.flow;
        }
        printf("%d %d
    ",res,tot);
        for(int i=0;i<tot;i++)printf("%d %d %d
    ",ans[i][0],ans[i][1],ans[i][2]);
        return 0;
    }


  • 相关阅读:
    定义一个动态的二维数组?
    学习STL map, STL set之数据结构基础
    CMM
    详细解说STL string
    仔细选择你的容器
    C++字符串类string简析
    流氓软件及反流氓软件的技术分析
    标准C++的类型转换符:static_cast、dynamic_cast、reinterpret_cast和const_cast
    C++资料下载
    unix系统操作命令大全
  • 原文地址:https://www.cnblogs.com/01world/p/5762854.html
Copyright © 2011-2022 走看看