poj 3436 最大流+拆点(安装电脑)

题意(参考北京大学郭炜老师课件):每个工厂有三个动作:1)接收原材料;2)生产 ;3)将其产出的半成品给其他机器,或产出成品。这三个过程都对应不同的流量。对第一组输入数据的分析:

输入:

3 4
15 0 0 0 0 1 0
10 0 0 0 0 1 1
30 0 1 2 1 1 1
 3  0 2 1 1 1 1
输出:
25 2
1 3 15
2 3 10
输入解释:电脑由3个部件组成,共有4台机器,1号机器产量15, 能给空电脑加上2号部件;2号机器能给空电脑加上2号部件和3号部件,;3号机器能把具有1个2号部件和3号部件有无均可的电脑变成成品;4号机器能把具有1个3号部件和2号部件有无均可的电脑变成成品.
输出:单位时间最大产量25,有两台机器有协作关系:
1号机器单位时间内要将15个电脑给3号机器加工
2号机器单位时间内要将10个电脑给3号机器加工
注意:输出可能由多种可能,输出其中任意一种即可,而且协作关系的顺序随意。所以对于上述输入,如下输出也是正确的:

25 3
2 4 3
2 3 7
1 3 15

思路:

1) 添加一个原点S,S提供最初的原料 00000...
2) 添加一个汇点T, T接受最终的产品 11111....
3) 将每个机器拆成两个点: 编号为i的接收节点,和编号为i+n的产出节点(n是机器数目),前者用于接收原料,后者用于提供加工后的半成品或成品。这两个点之间要连一条边,容量为单位时间产量Qi
4) S 连边到所有接收 "0000..." 或 "若干个0及若干个2" 的机器,容量为无穷大
5) 产出节点连边到能接受其产品的接收节点,容量无穷大
6) 能产出成品的节点,连边到T,容量无穷大。
7) 求S到T的最大流

#include <stdio.h>
#include <string.h>
#define min(a,b) a<b?a:b
#define N 105
#define INF 0x3fffffff
struct edge{
	int y,c,next;
}e[N*N];
int p,n,top;
int in[N/2][11],out[N/2][11],w[N/2];
int index[N],first[N],pre[N],a[N],q[200000];
int iszero(int x[11]){//可以接受空电脑,也就是连接到超级源点
	int i,temp=0;
	for(i = 1;i<=p;i++)
		if(x[i] == 1)
			return 0;
	return 1;
}
int isone(int x[11]){//所有的零件装配齐全,连接到超级汇点
	int i,temp=0;
	for(i = 1;i<=p;i++)
		temp += x[i];
	if(temp==p)
		return 1;
	return 0;
}
void add(int x,int y,int c){
	e[top].y = y;
	e[top].c = c;
	e[top].next = first[x];
	first[x] = top++;
	e[top].y = x;
	e[top].c = 0;
	e[top].next = first[y];
	first[y] = top++;
}
int test(int x[11],int y[11]){//测试y能否接收x的输出
	int i;
	for(i = 1;i<=p;i++)
		if(x[i]+y[i] == 1)
			return 0;
	return 1;
}
void maxflow(int s,int t){
	int i,j,res=0,sum=0,now,front,rear;
	front = rear = -1;
	while(1){
		memset(a,0,sizeof(a));
		memset(pre,0,sizeof(pre));
		q[++rear] = s;
		a[s] = INF;
		while(front < rear){
			now = q[++front];
			for(i = first[now];i!=-1;i=e[i].next)
				if(!a[e[i].y] && e[i].c>0){
					q[++rear] = e[i].y;
					a[e[i].y] = min(a[now],e[i].c);
					pre[e[i].y] = now;
					index[e[i].y] = i;
				}
		}
		if(!a[t])
			break;
		res += a[t];
		for(i = t;i!=0;i=pre[i]){
			e[index[i]].c -= a[t];
			e[index[i]^1].c += a[t];
		}
	}
	for(i = t-2;i>0;i-=2)//计算有协作关系的数量
		for(j = first[i];j!=-1;j=e[j].next)
			if(e[j].c>0 && e[j].y!=0 && e[j].y!=i+1)//奇数节点的反向边有流量且不是留回源点的就是我们要找的
				sum++;
	printf("%d %d\n",res,sum);
}
void print(int t){
	int i,j;
	for(i = t-2;i>0;i-=2)
		for(j = first[i];j!=-1;j=e[j].next)
			if(e[j].c>0 && e[j].y!=0 && e[j].y!=i+1)
				printf("%d %d %d\n",e[j].y/2,(i+1)/2,e[j].c);//奇数节点的反向边有流量且不是留回源点的就是我们要找的
}
int main(){
	freopen("a.txt","r",stdin);
	while(scanf("%d %d",&p,&n)!=EOF){
		int i,j;
		top = 0;
		memset(first,-1,sizeof(first));
		for(i = 1;i<=n;i++){
			scanf("%d",&w[i]);
			for(j = 1;j<=p;j++)
				scanf("%d",&in[i][j]);
			for(j = 1;j<=p;j++)
				scanf("%d",&out[i][j]);
			if(iszero(in[i]))
				add(0,2*i-1,INF);
			if(isone(out[i]))
				add(2*i,2*n+1,INF);
			add(2*i-1,2*i,w[i]);
		}
		for(i = 1;i<=n;i++)
			for(j = 1;j<=n;j++)
				if(test(out[i],in[j]))
					add(2*i,2*j-1,INF);
		maxflow(0,2*n+1);
		print(2*n+1);
	}
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值