POJ3436,ACM Computer Factory(最大流)

题意:一家电脑厂里,有N台组装电脑的机器,电脑由P个零件组成,每台机器有in[P]和out[P]两个参数,表示该机器需要in[P]所指示的零件,产出out[P]所指示的零件,同时每台机器每小时最多产出Q[i]组out[P]零件。问如何连接各个机器,使得每小时产出的电脑数量最多,同时输出连接的次数与方式。

这道题是网络流最大流问题,但是有一点小变化。在最基本的最大流问题中,边是带固定容量的,但在本题中,边并没有固定容量,反而是顶点带有容量,边的容量是不定的,可以分配的。因此需要对最大流算法做一点小小的修改才可。

此外,由于这道题本身没有源点和汇点,因此我们可以在网络图中增添源点s=1,汇点t=n+2,其他顶点编号为2-n+1,同时,源点s有对应的out[1…P]=0,没有in[P],汇点t有对应的in[1…P]=1,没有out[P]。

代码如下:

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<queue>
#include<cstring>
#include<stack>
using namespace std;
const int maxn=55;
const int maxm=3e3+5;
const int inf=0x7f7f7f7f;
typedef long long ll;
int w[maxn],in[maxn][11],out[maxn][11];
int head[maxn],cnt;	
struct Edge
{
	int u,v,cap,next;	//u为起点,v为终点,cap为该边所连起点的容量,next为起点相同的下一边
}e[maxm];

struct Dinic		//最大流Dinic算法,封装在结构体中 
{
	int s,t,num;		//源点,汇点 
	int dis[maxn],cur[maxn];	//dis[]用于构造层次网络,表示顶点i到源点s的距离或相对s的层次
	stack<int> S;		//用于保存,输出路径 
	
	bool bfs()		//BFS构造层次网络 
	{
		memset(dis,0,sizeof(dis));
		queue<int> Q;
		Q.push(s);
		dis[s]=1;
		while(!Q.empty())
		{
			int fro=Q.front(); Q.pop();
			for(int i=head[fro]; i ;i=e[i].next)
			{
				if(e[i].cap&&!dis[e[i].v])
				{
					Q.push(e[i].v);
					dis[e[i].v]=dis[fro]+1;
				}
			}
		}
		return dis[t]!=0;	//dis[t]=0说明此时已经没有从源点到汇点的路径,即不存在增广路了,整个Dinic算法可以结束 
	}
	
	void proc(int pos,int flow)		//找到增广路中的最小残流后,调用此函数,将起点为pos的边的容量都减去flow
	{
		for(int i=head[pos]; i ;i=e[i].next)
			e[i].cap-=flow;
	}
	
	void copy_track(stack<int> st,int temp)		//复制路径
	{
		st.pop(); st.pop();	//与s,t相连的边都不要,这里先弹出与t相连的边 
		while(!st.empty())
		{
			int u,v;
			u=st.top(); st.pop();
			v=st.top(); st.pop();
			if(u!=s)
			{
				num++;
				S.push(temp);
				S.push(v-1);
				S.push(u-1);
			}
		}
	}
	
	int dfs(int pos,int flow,stack<int> st)	//DFS在层次图中多次增广 
	{
		if(pos==t)	return flow;
		int ret=0;
		for(int i=cur[pos]; flow&&i ;i=e[i].next)
		{
			cur[pos]=i;
			if(e[i].cap&&dis[e[i].v]==dis[pos]+1)
			{
				st.push(e[i].v); st.push(e[i].u);	//一定要先于下一步深搜之前保存路径
				int temp=dfs(e[i].v,min(e[i].cap,flow),st);
				if(temp>0)
				{
					proc(e[i].u,temp);
					flow-=temp;	//多次增广的关键 
					if(e[i].v==t)
						copy_track(st,temp);	//复制路径 
					ret+=temp; 
				}
				else dis[e[i].v]=-1;
				st.pop(); st.pop();		//记得弹出
			}
		}
		return ret;
	}
	
	void maxflow(int n)
	{
		int ans=0;
		while(bfs())
		{
			stack<int> st;
			for(int i=1;i<=n;i++)	cur[i]=head[i];
			ans+=dfs(s,inf,st);
		}
		printf("%d %d\n",ans,num);
		while(!S.empty())
		{
			int u,v,w;
			u=S.top(); S.pop();
			v=S.top(); S.pop();
			w=S.top(); S.pop();
			printf("%d %d %d\n",u,v,w);
		}
	}
}dinic;

void add(int u,int v,int w)		//添加边 
{
	e[cnt].u=u; e[cnt].v=v; e[cnt].cap=w; e[cnt].next=head[u]; head[u]=cnt++;		
	e[cnt].u=v; e[cnt].v=u; e[cnt].cap=0;  e[cnt].next=head[v]; head[v]=cnt++; 		//反向边 
}
	
void init(int n,int p)		//初始化数据并建立网络图
{
	memset(head,0,sizeof(head));
	cnt=2; dinic.num=0;
	for(int i=1;i<=n+1;i++)
	{
		for(int j=2;j<=n+2;j++)
		{
			if(j==i)	continue;
			int flag=1;
			for(int k=1;k<=p;k++)
			{
				if(out[i][k]!=in[j][k])
				{
					if(in[j][k]!=2)
					{
						flag=0;
						break;
					}
				}
			}
			if(flag)
				add(i,j,w[i]);
		}
	}
	
}

int main()
{
	int n,p;
	while(~scanf("%d%d",&p,&n))
	{
		for(int i=1;i<=p;i++)
		{
			out[1][i]=0;
			in[n+2][i]=1;
		}
		w[1]=inf;		//这一步要记得
		for(int i=2;i<=n+1;i++)
		{
			scanf("%d",&w[i]);
			for(int j=1;j<=p;j++)
				scanf("%d",&in[i][j]);
			for(int j=1;j<=p;j++)
				scanf("%d",&out[i][j]);
		}
		init(n,p);
		dinic.s=1; dinic.t=n+2;
		dinic.maxflow(n+2);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值