【总结&模板】上下界网络流的一些注意点

以下的内容仅是一些对上下界网络流的补充说明,基础概念都没有涉及,但是有一些注意点。

这里的三道题(无源汇上下界可行流,有源汇上下界最大流,有源汇上下界最小流)是采用\(LOJ\)的模板,题号是\(115-117\)

\(LOJ115\) 无源汇有上下界可行流

题意:

给定一个无源汇的图,流量有上下界,问一个可行流。

知识点:

无源汇有上下界可行流

解法:

每个点的容量转化为上界\(-\)下界,并统计每个点的多余流量,新建源汇点\(SS,TT\)连那些多余的流量,然后跑最大流即可。如果那些源汇边上不满流,就没有可行流,否则在流量上加上该边的下界即可。

备注:

注意:

  1. 一个与意识上理解起来不同的地方,如果入流\(>\)出流,应该是\(SS\)\(i\)连边,否则是\(i\)\(TT\)连边。
  2. 跑完最大流后的图上,正边是残量网络,反边才是跑了的流量,用反边\(+\)下界才是答案(其实用上界\(-\)正边也对)。

代码:

#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;

const int maxn=210,maxm=31000,inf=0x7fffffff;
int n,m,SS,TT,tot,head[maxn],dis[maxn],cur[maxn],d[maxn];
struct node
{
	int nxt,to,w,rg;
}edge[maxm];
queue<int>q;

int read()
{
	int x=0;
	char c=getchar();
	while (c<48||c>57)
		c=getchar();
	while (c>=48&&c<=57)
		x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return x;
}

void add(int u,int v,int w,int rg=0)
{
	edge[++tot]=(node){head[u],v,w,rg};
	head[u]=tot;
}

void link(int u,int v,int w,int rg=0)
{
	add(u,v,w,rg);
	add(v,u,0,rg);
}

bool bfs()
{
	memset(dis,0,sizeof(dis));
	int i,u,v;
	q.push(SS);
	dis[SS]=1;
	while (!q.empty())
	{
		u=q.front();
		q.pop();
		for (i=head[u];i;i=edge[i].nxt)
		{
			v=edge[i].to;
			if (!dis[v]&&edge[i].w>0)
			{
				dis[v]=dis[u]+1;
				q.push(v);
			}
		}
	}
	return dis[TT];
}

int dfs(int u,int flow)
{
	if (u==TT)
		return flow;
	int res=flow,v,tmp;
	for (int &i=cur[u];i;i=edge[i].nxt)
	{
		v=edge[i].to;
		if (dis[v]==dis[u]+1&&edge[i].w>0)
		{
			tmp=dfs(v,min(res,edge[i].w));
			res-=tmp;
			edge[i].w-=tmp;
			edge[i^1].w+=tmp;
			if (!res)
				break;
		}
	}
	return flow-res;
}

void dinic()
{
	int i,tmp;
	while (bfs())
	{
		for (i=1;i<=TT;i++)
			cur[i]=head[i];
		tmp=dfs(SS,inf);
		if (!tmp)
			break;
	}
}

int main()
{
	int i,u,v,b,c;
	n=read(),m=read();
	tot=1;
	SS=n+1,TT=SS+1;
	for (i=1;i<=m;i++)
	{
		u=read(),v=read(),b=read(),c=read();
		link(u,v,c-b,b);
		d[v]+=b;
		d[u]-=b;
	}
	for (i=1;i<=n;i++)
		if (d[i]!=0)
		{
			if (d[i]<0)
				link(i,TT,-d[i]);
			else
				link(SS,i,d[i]);
		}
	dinic();
	int fl=0;
	for (i=head[SS];i;i=edge[i].nxt)
		if (edge[i].w)
		{
			fl=1;
			break;
		}
	if (!fl)
	for (i=head[TT];i;i=edge[i].nxt)
		if (edge[i^1].w)
		{
			fl=1;
			break;
		}
	if (fl)
		puts("NO");
	else
	{
		puts("YES");
		for (i=3;i<=2*m+1;i+=2)
			printf("%d\n",edge[i].w+edge[i].rg);
	}
	return 0;
}

\(LOJ116\) 有源汇上下界最大流

题意:

问有一个有源汇的图,流量有上下界,问最大流。

知识点:

有源汇上下界最大流

解法:

先判断可行性,还是加入\(SS\)\(TT\)来判断,但是\(S\)\(T\)处的流量不平衡,所以我们让\(T\)\(S\)连一条边\(INF\),这样就肯定平衡了,然后跑\(SS\)\(TT\)的最大流,判断是否可行,注意这条边的流量就是可行流的大小。然后删掉这条边,再跑\(S\)\(T\)的最大流,这个最大流\(+\)可行流就是答案了。

备注:

注意是删掉那条\(T-S\)的边,不是删掉\(T\)\(S\)或者是\(TT\)\(SS\)的这几个点。还有一种方法是不删这条边,然后直接跑\(S\)\(T\)的最大流,因为肯定不会选\(T-S\)这条边,但是反边一定选(为什么我还没懂),所以可行流就加上了。或者直接二分可行流的大小,然后赋值到那条\(T-S\)边上也可以(不太懂怎么实现)。

代码:

#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;

const int maxn=210,maxm=20500,inf=0x7fffffff;
int n,m,S,T,SS,TT,tot,head[maxn],cur[maxn],dis[maxn],d[maxn];
struct node
{
	int nxt,to,w;
}edge[maxm];
queue<int>q;

int read()
{
	int x=0;
	char c=getchar();
	while (c<48||c>57)
		c=getchar();
	while (c>=48&&c<=57)
		x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return x;
}

void add(int u,int v,int w)
{
	edge[++tot]=(node){head[u],v,w};
	head[u]=tot;
}

void lnk(int u,int v,int w)
{
	add(u,v,w);
	add(v,u,0);
}

bool bfs(int s,int t)
{
	memset(dis,0,sizeof(dis));
	int i,u,v;
	q.push(s);
	dis[s]=1;
	while (!q.empty())
	{
		u=q.front();
		q.pop();
		for (i=head[u];i;i=edge[i].nxt)
		{
			v=edge[i].to;
			if (!dis[v]&&edge[i].w>0)
			{
				dis[v]=dis[u]+1;
				q.push(v);
			}
		}
	}
	return dis[t];
}

int dfs(int u,int flow,int t)
{
	if (u==t)
		return flow;
	int v,tmp,res=flow;
	for (int &i=cur[u];i;i=edge[i].nxt)
	{
		v=edge[i].to;
		if (dis[v]==dis[u]+1&&edge[i].w>0)
		{
			tmp=dfs(v,min(res,edge[i].w),t);
			res-=tmp;
			edge[i].w-=tmp;
			edge[i^1].w+=tmp;
			if (!res)
				break;
		}
	}
	return flow-res;
}

int dinic(int s,int t)
{
	int i,ans=0,tmp;
	while (bfs(s,t))
	{
		for (i=1;i<=TT;i++)
			cur[i]=head[i];
		tmp=dfs(s,inf,t);
		if (!tmp)
			break;
		ans+=tmp;
	}
	return ans;
}

int main()
{
	int i,u,v,b,c,maxflow=0;
	n=read(),m=read(),S=read(),T=read();
	SS=n+1,TT=SS+1;
	tot=1;
	for (i=1;i<=m;i++)
	{
		u=read(),v=read(),b=read(),c=read();
		lnk(u,v,c-b);
		d[u]+=b,d[v]-=b;
	}
	for (i=1;i<=n;i++)
		if (d[i]!=0)
		{
			if (d[i]>0)
				lnk(i,TT,d[i]),maxflow+=d[i];
			else
				lnk(SS,i,-d[i]);
		}
	lnk(T,S,inf);
	maxflow-=dinic(SS,TT);
	if (maxflow)
	{
		puts("please go home to sleep");
		return 0;
	}
	maxflow=edge[tot].w;
	edge[tot].w=edge[tot^1].w=0;
	printf("%d\n",maxflow+dinic(S,T));
	return 0;
}

\(LOJ117\) 有源汇上下界最小流

题意:

问有一个有源汇的图,流量有上下界,问最小流。

知识点:

有源汇上下界最小流

解法:

前面连边同上下界最大流,考虑反边的最大减少量\(=\)正边的最小增加量,所以删边后可行流\(-\)\(T\)\(S\)的最大流即为最小流。

备注:

最好和\(0\)取个\(\max\)再输出,因为不知道最小流会不会\(<0\)不合法。

代码:

#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;

typedef long long ll;
const int maxn=50010,maxm=350020;
const ll inf=0x7fffffffffffffffll;
int n,m,S,T,SS,TT,tot,head[maxn],cur[maxn],dis[maxn];
ll d[maxn];
struct node
{
	int nxt,to;
	ll w;
}edge[maxm];
queue<int>q;

ll read()
{
	ll x=0;
	char c=getchar();
	while (c<48||c>57)
		c=getchar();
	while (c>=48&&c<=57)
		x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return x;
}

void add(int u,int v,ll w)
{
	edge[++tot]=(node){head[u],v,w};
	head[u]=tot;
}

void lnk(int u,int v,ll w)
{
	add(u,v,w);
	add(v,u,0);
}

bool bfs(int s,int t)
{
	memset(dis,0,sizeof(dis));
	int i,u,v;
	q.push(s);
	dis[s]=1;
	while (!q.empty())
	{
		u=q.front();
		q.pop();
		for (i=head[u];i;i=edge[i].nxt)
		{
			v=edge[i].to;
			if (!dis[v]&&edge[i].w>0)
			{
				dis[v]=dis[u]+1;
				q.push(v);
			}
		}
	}
	return dis[t];
}

ll dfs(int u,ll flow,int t)
{
	if (u==t)
		return flow;
	int v;
	ll tmp,res=flow;
	for (int &i=cur[u];i;i=edge[i].nxt)
	{
		v=edge[i].to;
		if (dis[v]==dis[u]+1&&edge[i].w>0)
		{
			tmp=dfs(v,min(res,edge[i].w),t);
			res-=tmp;
			edge[i].w-=tmp;
			edge[i^1].w+=tmp;
			if (!res)
				break;
		}
	}
	return flow-res;
}

ll dinic(int s,int t)
{
	int i;
	ll ans=0,tmp;
	while (bfs(s,t))
	{
		for (i=1;i<=TT;i++)
			cur[i]=head[i];
		tmp=dfs(s,inf,t);
		if (!tmp)
			break;
		ans+=tmp;
	}
	return ans;
}

int main()
{
	int i,u,v,b,c;
	ll maxflow=0;
	n=read(),m=read(),S=read(),T=read();
	SS=n+1,TT=SS+1;
	tot=1;
	for (i=1;i<=m;i++)
	{
		u=read(),v=read(),b=read(),c=read();
		lnk(u,v,c-b);
		d[u]+=b,d[v]-=b;
	}
	for (i=1;i<=n;i++)
		if (d[i]!=0)
		{
			if (d[i]>0)
				lnk(i,TT,d[i]),maxflow+=d[i];
			else
				lnk(SS,i,-d[i]);
		}
	lnk(T,S,inf);
	maxflow-=dinic(SS,TT);
	if (maxflow)
	{
		puts("please go home to sleep");
		return 0;
	}
	maxflow=edge[tot].w;
	edge[tot].w=edge[tot^1].w=0;
	printf("%lld\n",max(0ll,maxflow-dinic(T,S)));
	return 0;
}
posted @ 2020-02-16 16:23  MN2016  阅读(146)  评论(0编辑  收藏  举报