sgu194 无源汇上下界的最大流

/*   无源汇上下界最大流
   对于有流量上下限的无源的网络流的可行流转化为一般的有源汇点的最大流来做 
   (1)添加超级源点s和超级汇点t 
   (2)对于原有的边(u,v,l(u,v),c(u,v))(l为流量下限,c为流量上限),添加边(u,v,0,c-l); 
   (3)对于每个结点i,记w[i]=l(u,i)-l(i,v)=f(i,v)-f(u,i);出-入   
      若w[i]>0,添加边(S,i,w[i]),若w[i]<0,添加边(i,T,-w[i]); 
   (4)求解s-t的最大流; 
   (5)当且仅当s的出边和t的入边满流,原流量限制的无源网络流可行流有解; 实际上判断其中一个条件即可。
   (6)一组可行流的解为: 
      对于每条流量边(u,v),可行流量为l(u,v)+其构造的图中的流量. 
   
   */  
#include "stdafx.h"
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN=200000;
const int maxn=300;
struct EDGE
{
	int from,to,next,flow;
}edge[MAXN];
int head[maxn],cnt;
int s,t,tot,sum;
int pre[maxn];
int gap[maxn];
int cur[maxn];
int level[maxn];
int w[maxn],l[MAXN];
inline void add(int u,int v,int flow)
{
	edge[cnt].from=u;
	edge[cnt].to=v;
	edge[cnt].flow=flow;
	edge[cnt].next=head[u];
	head[u]=cnt++;

	edge[cnt].from=v;
	edge[cnt].to=u;
	edge[cnt].flow=0;
	edge[cnt].next=head[v];
	head[v]=cnt++;
}
void build_graph(int n,int m)
{
	s=0,t=n+1,tot=n+2,cnt=0;  //(1)添加超级源点s和超级汇点t 
	memset(pre,-1,sizeof(pre));
	memset(gap,0,sizeof(gap));
	memset(level,0,sizeof(level));
	memset(head,-1,sizeof(head));
	gap[0]=tot;
	for(int i=0;i<m;i++)
	{
		int u,v,c;
		scanf("%d%d%d%d",&u,&v,&l[i],&c);//因为上界暂不需要,不需要储存
		add(u,v,c-l[i]);//(2)对于原有的边(u,v,l(u,v),c(u,v))(l为流量下限,c为流量上限),添加边(u,v,0,c-l); 
		w[u]-=l[i];  //w[i]=l(u,i)-l(i,v)
		w[v]+=l[i];
	}
	for(int i=1;i<=n;i++)   //若w[i]>0,添加边(S,i,w[i]),若w[i]<0,添加边(i,T,-w[i]); 
	{
		if(w[i]>0) add(s,i,w[i]);//f(u,i)+w[i]=f(i,v)
		if(w[i]<0) add(i,t,-w[i]),sum+=-w[i];//f(u,i)=f(i,v)+w[i]
	}
}
int sap()
{
	int u=s,ans=0,inf=INT_MAX;
	for(int i=0;i<tot;i++)
		cur[i]=head[i];
	while(level[s]<tot)
	{
		bool flag=0;
		for(int &i=cur[u];i!=-1;i=edge[i].next)
		{
			int v=edge[i].to;
			if(edge[i].flow>0&&level[u]==level[v]+1)
			{
				flag=1;
				if(edge[i].flow<inf)
					inf=edge[i].flow;
				pre[v]=u;
				u=v;
				if(u==t)
				{
					while(u!=s)
					{
						u=pre[u];
						edge[cur[u]].flow-=inf;
						edge[cur[u]^1].flow+=inf;
					}
					ans+=inf;
					inf=INT_MAX;
				}
				break;
			}
		}
		if(!flag)
		{
			if(--gap[level[u]]==0)
				break;
			int minn=tot;
			for(int i=head[u];i!=-1;i=edge[i].next)
			{
				int v=edge[i].to;
				if(edge[i].flow>0&&level[v]<minn)
				{
					minn=level[v];
					cur[u]=i;
				}
			}
			level[u]=minn+1;
			gap[minn+1]++;
			if(u!=s)
				u=pre[u];
		}
	}
	return ans;
}
int main()
{
	int n,m;
	scanf("%d %d",&n,&m);
	build_graph(n,m);
	if(sap()!=sum)
		printf("NO\n");  
	else
	{
		printf("YES\n");  
		for (int i=0; i<m; i++) //对于每条流量边(u,v),可行流量为l(u,v)+其构造的图中的流量. 
			printf("%d\n",edge[i*2+1].flow+l[i]);//前2*m都是原数据的边,edge[2*i+1].flow为构造图的流量
	}
	return 0;
}


 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值