从零单练网络流 第三章 有上下界的可行流

可行流无非就是把给定的一些条件转化为最大流可以完成的任务,著名的题目有SGU上的两道题P194 Reactor Cooling和P176 Flow Construction。第一题我本想依靠自己想出最大流的模型,不过最后还是看了题解。


这里简要讲一下P194 Reactor Cooling这道题的解法,这题在ACdream上也有,P1211。题意大概为给你n个点,m条边,每条边有上下界流,然后要保证每个点出流和入流一样。

建立超级源st和超级汇tr,对于每条边的容量为自己上下界流之差,记录每个点入出流情况。假如入流多,那st到该点加一条边即多出来的入流,反之,就该点引边向tr,值是这个绝对值。至于为什么这样,只有自己模拟一遍,自己说服自己了,说实在是说不清楚。不过有一点可以帮助你理解,这道题判断是否可行,是要st每条边都满流,之前因为st的边是由入流情况决定的,而其他边是上下界之差,即其他边是忽略了下界的判断的,那所有关于下界的判断全部集中于st引出的边,st引出的边正是保证他们下界之上的关键,而上下界之差的边则控制了上界。那为什么st引出的边保证了他们下界之上呢?因为假如每条边都是下界,那有些点必溢出,这些溢出的流则由超级源提供了。上下界之差控制上界应该好理解。

你理解了吗?


Reactor Cooling的代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<ctime>
#include<string>
#include<cstring>
#include<algorithm>
#include<fstream>
#include<queue>
#include<stack> 
#include<vector>
#include<cmath>
#include<iomanip>
#define rep(i,n) for(i=1;i<=n;i++)
#define MM(a,t) memset(a,t,sizeof(a))
#define INF 1e9
typedef long long ll;
#define mod 1000000007
using namespace std; 
int n,m,np,nc,st,tr; 
int res;
struct edge{
  int s,e,lest;
}eg[40020];
int w[220][220],inout[220];
int gap[220],dis[220],pre[220];
queue<int> Q;
void BFS(){
	int i,j;
	
	MM(gap,0); MM(dis,0);
	dis[tr]=0; gap[0]=1;
	while(!Q.empty()) Q.pop(); 
	Q.push(tr);
	while(!Q.empty()){
		int s,e=Q.front(); Q.pop();
		for(s=0;s<=n;s++)
		if(!dis[s] && w[s][e]){
			dis[s]=dis[e]+1;
			gap[dis[s]]++; Q.push(s);
		}
	} 
	//for(i=0;i<=n;i++) cout<<dis[i]<<' '; cout<<'\n';
}
int ISAP(){
  BFS();	
  int i,u=st,j,ans=0,md;
  pre[st]=-1;
  while(dis[u]<=n){
  	if(u==tr){
  		int minflow=INF;
	  	for(i=pre[u];i!=-1;u=i,i=pre[i])  minflow=min(minflow,w[i][u]);
		for(i=pre[u=tr];i!=-1;u=i,i=pre[i]){
			w[i][u]-=minflow;
			w[u][i]+=minflow;
		}	
		ans+=minflow;
	  }
    for(i=0;i<=n;i++)
      if(w[u][i]>0 && dis[i]+1==dis[u]) break;
    if(i<=n){
    	pre[i]=u;
    	u=i;
    }
    else{
    	if(--gap[dis[u]]==0) break;
    	for(md=n,i=0;i<=n;i++)
    	  if(w[u][i]>0) md=min(md,dis[i]);
  	    dis[u]=md+1;
  	    gap[dis[u]]++;
  	    if(u!=st) u=pre[u];
    }
  }
  return ans;
}
int main()
{
	int i,j,i1,i2,i3;
	string si;

    while(scanf("%d%d",&n,&m)!=EOF){
      MM(w,0); MM(inout,0);
      rep(i,m){
      	int s,e,v1,v2;
      	scanf("%d%d%d%d",&s,&e,&v1,&v2);
      	eg[i].s=s; eg[i].e=e; eg[i].lest=v1;
      	inout[s]-=v1; inout[e]+=v1;
      	w[s][e]=v2-v1;
      } 
      st=0; tr=n+1; n++;
	  rep(i,n-1)
	  if(inout[i]<0) w[i][tr]=-inout[i];
	  else           w[st][i]=inout[i];
	  ISAP();
	  bool ff=true;
	  rep(i,n-1)
	  if(w[st][i]!=0){
  	    ff=false;
		break;	
  	  } 
  	  if(!ff) printf("NO\n");
  	  else{
  	    printf("YES\n");
		rep(i,m) printf("%d\n",eg[i].lest+w[eg[i].e][eg[i].s]);	
  	  }
    }
	
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值