ZOJ - 2314 Reactor Cooling(有上下界网络流)

题目:

给n个点和m个边,每条边有流量上界和下界,问能否使这n个点形成一个流量循环,每个点流入等于流出,每条边都在界限之内。

分析:

典型的有上下界无源汇网络流。
法一:
建立源点 s s s 和汇点 t t t , 对于图中每条边 &lt; u , v &gt; &lt;u, v&gt; <u,v> ,拆成如下三条:

  • &lt; s , v &gt; &lt;s,v&gt; <s,v> ,容量为 l l l
  • &lt; u , t &gt; &lt;u,t&gt; <u,t> ,容量为 l l l
  • &lt; u , v &gt; &lt;u, v&gt; <u,v> ,容量为 r − l r - l rl
    其中前两条弧一般称为附加弧。
    然后对图跑从 s s s t t t 的最大流,如果所有附加弧都满流,则有可行流。这时,每条非附加弧的流量加上它的容量下界,就是原图中这条弧应该有的流量。

法二:
建立源点 s s s 和汇点 t t t ,对于每条边建立 &lt; u , v &gt; &lt;u,v&gt; <u,v> 容量为 r − l r-l rl 的边。此外,对于图中每个点,令 d [ i ] = ∑ i 节 点 所 有 入 流 下 界 和 − ∑ i 节 点 所 有 出 流 下 界 和 d[i] = \sum i节点所有入流下界和 - \sum i节点所有出流下界和 d[i]=ii
d [ i ] &gt; 0 d[i] &gt; 0 d[i]>0, 建立 &lt; s , i &gt; &lt;s,i&gt; <s,i> 容量为 d [ i ] d[i] d[i] 的边。
d [ i ] &lt; 0 d[i] &lt; 0 d[i]<0 ,建立 &lt; i , t &gt; &lt;i,t&gt; <i,t> 容量为 − d [ i ] -d[i] d[i] 的边。
然后跑 s s s t t t 的最大流,若附加边全部满流,即 m a x f l o w = ∑ d [ i ] &gt; 0 maxflow = \sum d[i] &gt;0 maxflow=d[i]>0之和,存在可行流。每条边流量同法一。

其实法一和法二本质是一样的,但法二建立的边更少,速度更快。

代码:

#include <bits/stdc++.h>
using namespace std;
#define ms(a,b) memset(a,b,sizeof(a))
#define lson rt*2,l,(l+r)/2
#define rson rt*2+1,(l+r)/2+1,r
typedef unsigned long long ull;
typedef long long ll;
const int MAXN = 205;
const int MAXM = 100005;
const int INF=0x3f3f3f3f;
int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x*=10;x+=ch-'0';ch=getchar();}
    return x*f;
}
struct Edge{
	int to,next,cap,flow;
	int id;
}edge[MAXM];
int tot,head[MAXN];
int Q[MAXN],cur[MAXN],dep[MAXN];
int n,m,S,T,N,to[MAXN];
void init(){
	tot = 2;
	memset(head,-1,sizeof head);
}
void addedge(int u, int v, int w, int id, int rw = 0) {
    edge[tot].to = v; edge[tot].cap = w; edge[tot].flow = 0; edge[tot].id = id;
    edge[tot].next = head[u]; head[u] = tot++;
    edge[tot].to = u; edge[tot].cap = rw; edge[tot].flow = 0; edge[tot].id = -1;
    edge[tot].next = head[v]; head[v] = tot++;
}
bool bfs(int s,int t,int n){
	int Front = 0, tail = 0;
	memset(dep,-1,sizeof(dep[0])*(n+1));
	dep[s] = 0;
	Q[tail++] = s;
	while(Front < tail){
		int u = Q[Front++];
		for(int i=head[u];i!=-1;i=edge[i].next){
			int v=edge[i].to;
			if(edge[i].cap > edge[i].flow && dep[v] == -1){
				dep[v] = dep[u]+1;
				if(v==t) return true;
				Q[tail++] = v;
			}
		}
	}
	return false;
}
int dfs(int u,int f){
	if(u==T)	return f;
	int used = 0, rflow = 0;
	for(int i=cur[u];i!=-1;i=edge[i].next){
		cur[u] = i;
		int v = edge[i].to, w = edge[i].cap - edge[i].flow;
		if (w>0 && dep[v] == dep[u]+1){
			if((rflow=dfs(v,min(w,f-used)))){
				used+=rflow;
				edge[i].flow += rflow;
				edge[i^1].flow -= rflow;
				if(used == f)	break;
			}
		}
	}
	if(!used)	dep[u] = -1;
	return used;
}
int dinic(int s,int t,int n){
	int maxflow = 0;
	while(bfs(s,t,n)){
		for(int i=0;i<=n;i++)	cur[i] = head[i];
		maxflow += dfs(s,INF);
	}
	return maxflow;
}
int low[MAXM], d[MAXN], id[MAXM];
int main(){
	// freopen("cooling.in","r",stdin);
	// freopen("cooling.out","w",stdout);
	int kase;
	kase = read();
	while(kase--) {
		n = read(); m = read();
		init();
		for(int i=0;i<=n;i++) d[i] = 0;
		for(int i=0;i<=m;i++) id[i] = 0;
		S = 0, T = n + 1, N = n + 1;
		for(int i=1;i<=m;i++) {
			int u,v,l,r;	u = read(); v = read(); l = read(); r = read();
			addedge(u,v,r-l,i);
			low[i] = l;
			d[u] -= l; d[v] += l;
			id[i] = tot-2;
		}
		int sum = 0;
		for(int i=1;i<=n;i++) {
			if(d[i] > 0 ) {
				sum += d[i];
				addedge(S,i,d[i],-1);
			}else if(d[i] < 0 ) {
				addedge(i,T,-d[i],-1);
			}
		}
		if(dinic(S,T,N) == sum) {
			puts("YES");
			for(int i=1;i<=m;i++) {
				printf("%d\n",low[i] + edge[id[i]].flow);
			}
		}else {
			puts("NO");
		}
		puts("");
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值