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;
}


原文地址:https://www.cnblogs.com/01world/p/5762854.html