网络流2:上下界可行流

网络流2 上下界可行流

无源汇上下界可行流

概念定义

对于一流网络 \(G=(V,E)\),每条边 \((u,v)\) 存在容量上界 \(c_u(u,v)\) 和容量下界 \(c_l(u,v)\),且不存在源点和汇点。

与原定义类似,满足容量限制和流量守恒的流称之为可行流。

  • 容量限制:在原定义的基础上,更改容量限制的定义:该可行流在任意边 \((u,v)\) 的流量 \(f(u,v)\) 满足 $c_l(u,v) \le f(u,v) \le c_u(u,v) $,则该流满足容量限制。
  • 流量守恒:与原定义相同,但是由于没有 \(s\)\(t\),所以所有点都需满足流量守恒。

思路推导

对于一对流网络和可行流 \((G,f)\),我们期望构造一对新的流网络和可行流 \((G',F')\) 满足与 \((G,f)\) 一一对应。

对于容量下界,我们十分讨厌。所以不妨在不等式两边同时减去容量下界,使得 $0 \le f(u,v)-c_l(u,v) \le c_u(u,v)-c_l(u,v) $,并记 \(f'(u,v) = f(u,v) - c_l(u,v)\)\(c'(u,v) = c_u(u,v)-c_l(u,v)\)。此时 \(f'\) 满足 $0 \le f'(u,v) \le c'(u,v) $,即满足容量限制。

但是,对于一点 \(u\),其流入量会减少 $\sum _{v \in V} c_l(v,u) $,流出量会减少 $\sum _{v \in V} c_l(u,v) $,净差 \(w_u\) 等于 $\sum _{v \in V} c_l(v,u) - \sum _{v \in V} c_l(u,v) $。

此时,不妨建立虚点 \(S'\)\(T'\),若

  • \(w_u = 0\),则无需操作。
  • \(w_u < 0\),则连接边 $c'(S',v) = -w $
  • \(w_u > 0\),则连接边 $c'(u,T') = w $

如果附加边满流,说明这一个点的流量平衡条件可以满足,否则这个点的流量平衡条件不满足。

此时进行 \(S'\)\(T'\) 的最大流,若附加边满流则存在可行流,否则不存在。

代码:

using Dinic::init;using Dinic::add;using Dinic::dinic;

int cl[M],cu[M],w[M];

void mian()
{
	init();
	read(n),read(m);
	int S=0,T=n+1;
	for(int i=1;i<=m;i++)
	{
		int u,v;
		read(u),read(v),read(cl[i]),read(cu[i]);
		cu[i]=cu[i]-cl[i];w[u]+=cl[i];w[v]-=cl[i];
		add(u,v,cu[i]);
	}
	
	int tot=0;
	for(int i=1;i<=n;i++)
	{
		tot+=abs(w[i]);
		if(w[i]>0)
		{
			add(i,T,w[i]);
		}
		else if(w[i]<0)
		{
			add(S,i,-w[i]);
		}
	}
	tot/=2;
	
	int res=dinic(S,T);
	if(res!=tot) puts("NO");
	else
	{
		puts("YES");
		for(int i=0;i<m*2;i+=2)
		{
			printf("%d\n",Dinic::e[i^1].c+cl[i/2+1]);
		}
	}
}

有源汇上下界可行流

与前问相比新增了源点 \(s\) 和汇点 \(t\)。请注意区分,我们的虚拟点是 \(S\)\(T\)

不妨连接 $c(t,s) = \infin $,这样这两点也流量守恒了

代码易写。

有源汇上下界最大流

与前问相比需要在满足容量限制的情况下使得源点 \(s\) 到汇点 \(t\) 的流尽可能大。

首先是需要跑一个可行流。然后删除掉辅助边(正常就不用管了)和 \((t,s)\)。然后,跑一个 \(s\)\(t\) 的最大流即可。

证明大致如下:核心难点在于 \(S\)\(T\) 的无增广路不等于 \(s\)\(t\) 的无增广路,因为 \(S,T\) 不满足流量守恒。但是考虑到增广路的“底层”是加法,而这里若将两个流做减法,就会发现由于这两个流都是可行流,所以 \(S\)\(T\) 都是满流,所以相减后容量为零,这两点就满足容量守恒了。

代码

using Dinic::init;using Dinic::add;using Dinic::dinic;

int cl[M],cu[M],w[M];

void mian()
{
	int t,s;
	init();
	read(n),read(m),read(s),read(t);
	int S=0,T=n+1;
	for(int i=1;i<=m;i++)
	{
		int u,v;
		read(u),read(v),read(cl[i]),read(cu[i]);
		cu[i]=cu[i]-cl[i];w[u]+=cl[i];w[v]-=cl[i];
		add(u,v,cu[i]);
	}
	
	int tot=0;
	for(int i=1;i<=n;i++)
	{
		tot+=abs(w[i]);
		if(w[i]>0)
		{
			add(i,T,w[i]);
		}
		else if(w[i]<0)
		{
			add(S,i,-w[i]);
		}
	}
	tot/=2;
	
	add(t,s,INF);
	
	int res=dinic(S,T);
	if(res!=tot) puts("No Solution");
	else
	{
		res=Dinic::e[Dinic::ecnt].c;
		Dinic::e[Dinic::ecnt].c=Dinic::e[Dinic::ecnt-1].c=0;
		res+=dinic(s,t);
		printf("%d\n",res);
	}
}

有源汇上下界最小流

类似的,只是将最大流变为最小流,可以为负数。

根据定义,不难想到转化:$f(s,t)=-f(t,s) $,那么最小的 $f(s,t) $ 不就是最大的 \(f(t,s)\) 吗?

代码

using Dinic::init;using Dinic::add;using Dinic::dinic;

int cl[M],cu[M],w[M];

void mian()
{
	int t,s;
	init();
	read(n),read(m),read(s),read(t);
	int S=0,T=n+1;
	for(int i=1;i<=m;i++)
	{
		int u,v;
		read(u),read(v),read(cl[i]),read(cu[i]);
		cu[i]=cu[i]-cl[i];w[u]+=cl[i];w[v]-=cl[i];
		add(u,v,cu[i]);
	}
	
	int tot=0;
	for(int i=1;i<=n;i++)
	{
		tot+=abs(w[i]);
		if(w[i]>0)
		{
			add(i,T,w[i]);
		}
		else if(w[i]<0)
		{
			add(S,i,-w[i]);
		}
	}
	tot/=2;
	
	add(t,s,INF);
	
	int res=dinic(S,T);
	if(res!=tot) puts("No Solution");
	else
	{
		res=Dinic::e[Dinic::ecnt].c;
		Dinic::e[Dinic::ecnt].c=Dinic::e[Dinic::ecnt-1].c=0;
		res-=dinic(t,s);
		printf("%d\n",res);
	}
}

参考资料

忘了……这篇文章好久之前写的,也没有进行大改。到现在还有点半成品,但是沃咕了

私密马赛 (>. <;)

updaye on 7/14

优化了代码,对有源汇上下界最小流添加了一句极为简短的转化分析

posted @ 2026-07-12 13:51  FarrisL  阅读(5)  评论(0)    收藏  举报