【无源汇上下界最大流】SGU 194 Reactor Cooling

本文介绍了一种解决无源汇上下界最大流问题的方法,通过调整边的容量下界为0并增设超级源点和终点,确保流量守恒。文中详细解释了建图模型,并提供了一个具体的实现案例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:

  http://acm.sgu.ru/problem.php?contest=0&problem=194

题目大意:

  n个点n<20000!!!不是200!!!RE了无数次,m条边(管子)(m范围好像没说,我开了10^6),每个点流入的和流出的液体要相等,每条边(管子)有上下界流量,问是否有解,有解YES无解NO,有解还要输出每条边的流量。

  题目输入样例略坑,我以为要多组数据输入输出TEST #1 什么的。后来仔细看了Input发现只有一组吧应该,保险起见还是写了多组,但是不用读入TEST是肯定的。

题目思路:

  【无源汇上下界最大流】

  直接建图套最大流模版即可。

  最近刚好在搞上下界问题,等到都做完了写个总结吧。

  建图模型:(学习自Mr. Ant http://www.cnblogs.com/kane0526/archive/2013/04/05/3001108.html

  以前写最大流默认下界为0,而这里的下界却不为0,所以我们要进行再构造让每条边的下界为0,这样做是为了方便处理。

  对于每条边(管子)有一个上界容量c和一个下界容量b,我们让这条边(管子)的容量下界变为0,上界为b-c。

  可是这样做的话流量就不守恒了,为了再次满足流量守恒,增设一个超级源点S和一个超级终点T。

  我们开设一个数组in[]来记录每个节点的流量情况。

  in[i]=Σi入下界i出下界(i节点所有入流下界之和-i节点所有出流下界之和)。

  当in[i]大于0的时候,S到i连一条流量为in[i]的边。

  当in[i]小于0的时候,i到T连一条流量为-in[i]的边。

  最后对(S,T)求一次最大流即可,当所有附加边全部满流时(S的出边都满流时),有可行解。

  

//
//by coolxxx
//
#include<iostream>
#include<algorithm>
#include<string>
#include<iomanip>
#include<memory.h>
#include<time.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdbool.h>
#include<math.h>
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
#define abs(a) ((a)>0?(a):(-(a)))
#define lowbit(a) (a&(-a))
#define sqr(a) (a)*(a)
#define swap(a,b) (a)^=(b),(b)^=(a),(a)^=(b)
#define eps 1e-8
#define MAX 0x7f7f7f7f
#define PI 3.1415926535897
#define N 20004
#define M 1000004
using namespace std;
int n,m,cas,lll,ans;
int S,T,nn;
int last[N],in[N],d[N],vd[N],low[N];
char s[N];
bool inq[N];
struct xxx
{
	int next,to,f;
}e[M];
void add(int x,int y,int f)
{
	e[++lll].next=last[x];
	last[x]=lll;
	e[lll].to=y;
	e[lll].f=f;
}
void link(int x,int y,int f)
{
	add(x,y,f);
	add(y,x,0);
}
void build()
{
	int i,f,x,y,b,c;
	lll=1;ans=0;
	memset(low,0,sizeof(low));
	memset(in,0,sizeof(in));
	memset(last,0,sizeof(last));
	memset(e,0,sizeof(e));
	memset(d,0,sizeof(d));
	memset(vd,0,sizeof(vd));
	scanf("%d",&m);
	for(i=1;i<=m;i++)
	{
		scanf("%d%d%d%d",&x,&y,&b,&c);
		in[x]-=b;in[y]+=b;
		link(x,y,c-b);
		low[i]=b;
	}
	S=n+1;
	T=n+2;
	nn=n+2;
	for(i=1;i<=n;i++)
	{
		if(in[i]>0)link(S,i,in[i]);
		if(in[i]<0)link(i,T,-in[i]);
	}
}
int sap(int u,int f)
{
	int i,tt,asp=0,mix=nn-1;
	if(u==T)return f;
	for(i=last[u];i;i=e[i].next)
	{
		if(e[i].f>0)
		{
			if(d[u]==d[e[i].to]+1)
			{
				tt=sap(e[i].to,min(f-asp,e[i].f));
				asp+=tt;
				e[i].f-=tt;
				e[i^1].f+=tt;
				if(asp==f || d[S]==nn)
					return asp;
			}
			mix=min(mix,d[e[i].to]);
		}
	}
	if(asp!=0)return asp;
	if(!--vd[d[u]])d[S]=nn;
	else vd[d[u]=mix+1]++;
	return asp;
}
int main()
{
	#ifndef ONLINE_JUDGE
//	freopen("1.txt","r",stdin);
//	freopen("2.txt","w",stdout);
	#endif
	int i,j,f;
//	while(~scanf("%s",s))
	while(~scanf("%d",&n) && n)
	{
		build();
		vd[0]=nn;
		while(d[S]<nn)
		{
			f=sap(S,MAX);
			ans+=f;
		}
		for(i=last[S],f=1;i;i=e[i].next)
		{
			if(e[i].f>0)
			{
				f=0;
				break;
			}
		}
		if(!f)
			puts("NO");
		else
		{
			puts("YES");
			for(i=1;i<=m;i++)
				printf("%d\n",low[i]+e[i+i+1].f);
		}
	}
	return 0;
}


/*
//

//
*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值