POJ - 3436 ACM Computer Factory 最大流,拆点,输出路径,dinic

题目链接

POJ-3436

题意

有n个工厂加工商品,商品有m个零件,工厂有最大产能q。告诉你每个工厂能接受加工成什么样子的半成品,以及经这个工厂加工之后的产品情况,问最大能生产多少商品。

思路

要拆点的。。第一次没拆也ac了,数据没卡这个。

先附POJ上一组hack数据,应输出10.

2 4
10 0 0 0 1
10 0 0 0 0
10 0 1 1 1
10 0 1 1 1

最大流题目,关键在于建图,这里先说一下建图部分

  1. 拆点拆成in和out两组,1-n代表in,n-2n代表out。每个工厂的in向out连边,容量为最大产能。
  2. 建立超级源汇,源点向所有可以从零开始加工的工厂的in连边(具体到输入数据,就是工厂输入的m个零件都为0或者2)。所有加工完毕的工厂的out(产出的m个零件都为1的)向汇点连边。容量inf或者最大产能均可
  3. 工厂间连边,枚举每个工厂A的out,向所有能接受这个A厂产品的工厂B的in连边(A厂输出产品m零件是0的,B厂接受的产品只能是0或2,A产出的是1的,B接受的只能是1或2,A产出的是2的,对B无要求)。容量可以inf,也可以是最大产能。

跑一遍最大流算法就可以求出答案,此外,这题还要求输出所有工厂连接,也就是最大流所有用到建图第三步的路径。我们在建图的时候都建立了反向边,反向边初始容量为0,如果反向边容量不为0了,那么说明这条边一定用到了,而且具体用了多少就是反向边的容量。遍历所有边,输出即可。

代码

#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<string>
#include<queue>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib> 
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
using namespace std;
	typedef long long ll;
	const int inf=0x3f3f3f3f;
	const int maxn=200;
	const int maxe=100010;
	
	int ansu[maxe],ansv[maxe],answ[maxe];//最终输出 
	int nodev[maxn];//产能 
	int in_[maxn][15],out_[maxn][15];//输入输出产品标准 

	int head[maxn],cnt;
	struct Edge{
    
    
		int u;//改了下链式前向星,用于输出 
		int v;
		int w;
		int next;
	}edge[maxe];

	int n,m,s,t;
	ll maxflow;
	int deep[maxn];
	int now[maxe];

	void init(){
    
    
		memset(head,-1,sizeof(head));
		cnt=0;//我习惯从0开始建边,反向边就是1,3,5,7.。。。。 
		maxflow=0; 
		return ;	
	}
	void add(int u,int v,int w){
    
    
		edge[cnt].u=u;
  		edge[cnt].v=v;
		edge[cnt].w=w;
		edge[cnt].next=head[u];
		head[u]=cnt++;
	}

	inline bool bfs(){
    
    
    	memset(deep,0x3f,sizeof(deep));
    	queue<int>q;
    	q.push(s);deep[s] = 0;now[s] = head[s];
    	while(q.size()){
    
    
        	int x = q.front();q.pop();
        	for(int i=head[x];i!=-1;i=edge[i].next){
    
    
        	    int y=edge[i].v;
         	    if(edge[i].w>0&&deep[y]==inf){
    
    
         	    	q.push(y);
        	        now[y]=head[y];
         	       	deep[y]=deep[x]+1;
        	        if(y==t)	return 1;
				}
        	}
    	}
    	return 0;
	}
	ll dfs(int x,int flow){
    
    
    	if(x==t)	return flow;
    	ll ans = 0,k,i;
    	for(i=now[x];i!=-1&&flow;i=edge[i].next){
    
    
        	now[x]=i;
        	int y=edge[i].v;
        	if(edge[i].w>0&&(deep[y]==deep[x]+1)){
    
    
        	    k=dfs(y,min(flow,edge[i].w));
         		if(!k)	deep[y]=inf;
            	edge[i].w-=k;
            	edge[i^1].w+=k;
            	ans+=k;
            	flow-=k;
        	}
    	}
    	return ans;
	}	
	void dinic(){
    
    
    	while(bfs())
    	    maxflow+=dfs(s,inf);
	}
	
	int main(){
    
    	
		IOS
		cin>>m>>n;
		s=n+n+1,t=n+n+2;
    	init();
    	for(int i=1;i<=n;i++){
    
    
    		cin>>nodev[i];
    		for(int j=1;j<=m;j++)
    			cin>>in_[i][j];
    		for(int j=1;j<=m;j++)
    			cin>>out_[i][j];
		}
		//工厂间边 
		for(int i=1;i<=n;i++){
    
    
			for(int j=1;j<=n;j++){
    
    
				bool ok=1;
				for(int w=1;w<=m;w++){
    
    
					if((out_[i][w]==1&&in_[j][w]==0)||(out_[i][w]==0&&in_[j][w]==1)){
    
    
						ok=0;
						break;
					}
				}
				if(ok){
    
    
					add(i+n,j,min(nodev[i],nodev[j]));
					add(j,i+n,0);
				}
			}
		}
		//到这里为止,是答案需要我们输出的边,底下的不能输出 
		int cnt2=cnt;
		//源汇连边 
		for(int i=1;i<=n;i++){
    
    
			bool ok=1;
			for(int w=1;w<=m;w++)
				if(in_[i][w]==1){
    
    
					ok=0;
					break;
				}
			if(ok){
    
    
				add(s,i,nodev[i]);
				add(i,s,0);
			}
			ok=1;
			for(int w=1;w<=m;w++)
				if(out_[i][w]==0){
    
    
					ok=0;
					break;
				}
			if(ok){
    
    
				add(i+n,t,nodev[i]);
				add(t,i+n,0);
			}
		}
		//in-out边 
		for(int i=1;i<=n;i++){
    
    
			add(i,i+n,nodev[i]);
			add(i+n,i,0);
		}
    	dinic();
    	int mc=0;//边数量
		//1,3,5,7,9.。。是反向边 
    	for(int i=1;i<cnt2;i+=2){
    
    
    		if(edge[i].w){
    
    
				ansu[mc]=edge[i].v-n;
				ansv[mc]=edge[i].u;
				answ[mc]=edge[i].w;
    			mc++;
			}
		} 
    	cout<<maxflow<<" "<<mc<<endl;
    	for(int i=0;i<mc;i++)
    		cout<<ansu[i]<<" "<<ansv[i]<<" "<<answ[i]<<endl;
    	return 0;
	}

猜你喜欢

转载自blog.csdn.net/TheSunspot/article/details/108127317
今日推荐