POJ 3436 ACM Computer Factory EK .

题目地址:http://poj.org/problem?id=3436

写了起码4hour+....

假设两个超级汇点源点,到000,和111点

因为p最大只有10,所以一个整数就能保存

保存点比较麻烦,一开始想的是map 给每个点一个id,但是两个相同的点,比如说010,不能看成同一个点,因为机器都不同,等下路径都找不了了

所以用in[],out[]保存出来的点和进去的点,当out[u]==in[v]说明u-->v 

还要输出路径,也就是协作关系:

1)方法一就是备份初始的图,当流量变少了,说明肯定流过

2)方法二就是根据剩下来的残余网络,如果反向边G[v][u]>0 ,说明u->v流过,这样可以省一个数组空间


然后我一直错的一个点就是,一直写超级源点到000而忽略了存在2的情况如020 ,超级汇点也是如此

#include<iostream>
#include<queue>
#include<cmath>

#include<cstring>
#include<cstdio>
using namespace std;
const int maxn=100+5;
const int INF=1000000000;
int G[maxn][maxn];
int PriG[maxn][maxn];
int prev[maxn];    //记录前驱节点,记录增广路径 
bool vis[maxn];
int read(int k,int& x)  //if k>0 in,else out
{
	bool s=false,t=false;
	if(k>0) s=true;
	else    t=true;
	x=0;int n=1;
	for(int i=0;i<fabs(k);i++){
		cin>>n;
		x=x*10+n;
		if(k>0&&n==1) s=false; 
		if(k<0&&n==0) t=false;
	}
	if(s) return 0;   //is  000  注意其中有2也算 
	if(t) return 1;   //is  111  
	return -1;
}
bool check(int n1,int n2)
{
	int i,j;
	for(;;)
	{
		if(n1==0&&n2==0) break;
		i=n1%10,j=n2%10;
		if(i+j==1)  return false;
		n1/=10;
		n2/=10;
	}
	return true;
}
int Augment(int s,int t)      //增广 
{
	queue<int> Q;
	bool isFound=false;  //标记是否找到了增广路 
	memset(vis,false,sizeof(vis));
	memset(prev,-1,sizeof(prev));
	
	Q.push(s);
	vis[s]=true;
	while(!Q.empty())
	{
		int u=Q.front(); Q.pop();
		if(u==t) {    //找到增广路径了 
			isFound=true;
			break;
		}
		for(int v=0;v<=t;v++)
		if(u!=v&&G[u][v]>0&&!vis[v]){
			prev[v]=u;
			
			vis[v]=true;
			Q.push(v); 
		}
	}
	if(!isFound) return 0;   //无路可走 
	int MinFlow=INF;   

	int u=t;
	while(u!=s){    //计算途中最小流 
		MinFlow=min(MinFlow,G[prev[u]][u]);
		u=prev[u];
	}
	
	u=t;
	while(u!=s){
		G[prev[u]][u]-=MinFlow;  //更新途径路的流量 
		G[u][prev[u]]+=MinFlow;  //加上反向边 
		u=prev[u];
	}
	return MinFlow;
}
void printPath(int m)
{
	vector<int> s,e,w;
	for(int i=1;i<=m;i++) 
	for(int j=1;j<=m;j++)
		if(i!=j&&PriG[i+m][j]>G[i+m][j])  //in->out 反向边
		{
			s.push_back(i);
			e.push_back(j);
			w.push_back(PriG[i+m][j]-G[i+m][j]);
		}
	cout<<s.size()<<endl;
	for(int i=0;i<s.size();i++)
		printf("%d %d %d\n",s[i],e[i],w[i]);
}
int in[maxn];
int out[maxn];
int main()
{
	int n,m;
	while(cin>>n>>m)
	{
		int u,v,w;
		int s=0,t=2*m+1;
		for(int i=1;i<=m;i++){
			cin>>w;
			G[i][i+m]+=w;  //1~m是in,m+1~2*m是out ,源点是0,汇点是2*m+1 
			if(read(n,in[i])==0) G[s][i]+=INF;
			if(read(-n,out[i])==1) G[i+m][t]+=INF;   
		}
		for(int i=1;i<=m;i++)
		for(int j=1;j<=m;j++)
			if(check(out[j],in[i])) G[j+m][i]+=INF;

		memcpy(PriG,G,sizeof(G));
		int MaxFlow=0;
		int aug;
		while(aug=Augment(s,t)) 
			MaxFlow+=aug;     //加上每次增广的流 
		cout<<MaxFlow<<' ';
		printPath(m);
	}
	return 0;	
} 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值