有上下界的网络流--学习笔记

无源汇有上下界可行流

LOJ #115 无源汇有上下界可行流
题目描述

n个点,m条边,每条边 e 有一个流量下界 lower ( e ) \text{lower}(e) lower(e) 和流量上界 upper ( e ) \text{upper}(e) upper(e) ,求一种可行方案使得在所有点满足流量平衡条件的前提下,所有边满足流量限制。

输出格式

如果无解,输出一行 NO。
否则第一行输出 YES,之后 m 行每行一个整数,表示每条边的流量。


既然每条边都有强制的流量的下界,
那么假设先让每条边都有大小等于下界的流量,显然此时并不一定满足流量平衡

为了保证流量平衡,显然要继续为每条边分配流量
而每条边还能继续分配的流量大小为 u p p e r i − l o w e r i upper_i-lower_i upperiloweri
所以对给定的每条边,建边 u u u v v v,容量为流量上界-流量下界( u p p e r i − l o w e r i upper_i-lower_i upperiloweri)

建立超级源点(ss)和超级汇点(tt)
d e g [ u ] deg[u] deg[u]表示在每条边流量大小等于下界的情况下,点 u u u总流入-总流出的大小
d e g [ u ] > 0 deg[u]>0 deg[u]>0,则建边 s s ss ss u u u,容量为 d e g [ u ] deg[u] deg[u]
d e g [ u ] &lt; 0 deg[u]&lt;0 deg[u]<0,则建边 u u u t t t,容量为 − d e g [ u ] -deg[u] deg[u]
这样就相当于表示出了每条边都有等于下界的流量后的等效情况

建立这样的网络后,跑 s s ss ss t t tt tt的最大流
检查此时 s s ss ss的出边是否都满流(即是否有 f ( s s , i ) = = c ( s s , i ) f(ss,i)==c(ss,i) f(ss,i)==c(ss,i))
若是则表示有可行方案
此时每条边的流量就是 在已有下界大小的流量后 还需要增加的流量
所以一种可行方案中每条边的流量就是 流量下界+网络中该边流量大小

判断是否满流可以判断是否有 m a x f = = ∑ d e g [ u ] &gt; 0 d e g [ u ] maxf==\sum_{deg[u]&gt;0}deg[u] maxf==deg[u]>0deg[u]

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long lt;

int read()
{
    int f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return f*x;
}

const int inf=1e9;
const int maxn=100010;
int n,m;
struct node{int v,f,nxt;}E[maxn<<1];
int head[maxn],tot=1;
int low[maxn],deg[maxn];
int lev[maxn],sum;

void add(int u,int v,int f)
{
	E[++tot].nxt=head[u];
	E[tot].v=v; E[tot].f=f;
	head[u]=tot; 
}

bool bfs(int s,int t)
{
    queue<int> q; q.push(s);
    memset(lev,-1,sizeof(lev)); lev[s]=0;
 
    while(!q.empty())
    {
        int u=q.front(); q.pop();
        for(int i=head[u];i;i=E[i].nxt)
        {
            int v=E[i].v;
            if(lev[v]==-1&&E[i].f)
            {
                lev[v]=lev[u]+1;
                if(v==t)return true;
                q.push(v);
            }
        }
    }
    return false;
}
 
int dfs(int u,int cap,int t)
{
    if(u==t) return cap;
    int flow=cap;
    for(int i=head[u];i;i=E[i].nxt)
    {
        int v=E[i].v;
        if( lev[v]==lev[u]+1&&flow&&E[i].f>0)
        {
            int f=dfs(v,min(flow,E[i].f),t);
            E[i].f-=f; E[i^1].f+=f; 
            flow-=f;
        }
    }
    return cap-flow;
}

int dicnic(int s,int t)
{
	int maxf=0;
	while(bfs(s,t)) maxf+=dfs(s,inf,t);
	return maxf;
}

int main()
{
	n=read();m=read();
	for(int i=1;i<=m;++i)
	{
		int u=read(),v=read(),l=read(),r=read();
		add(u,v,r-l); add(v,u,0);
		deg[u]-=l; deg[v]+=l;
		low[i]=l;
	}
	
	int ss=0,tt=n+1;
	for(int i=1;i<=n;++i)
	{
		if(deg[i]>0) add(ss,i,deg[i]),add(i,ss,0),sum+=deg[i];
		else if(deg[i]<0) add(i,tt,-deg[i]),add(tt,i,0);
	}
	
	int maxf=dicnic(ss,tt);
	if(maxf!=sum) printf("NO");
	else{
		printf("YES\n");
		for(int i=1,k=2;i<=m;++i,k+=2)
		printf("%d\n",E[k^1].f+low[i]);
	}
	return 0;
}

有源汇有上下界最大流

LOJ#116 有源汇有上下界最大流
题目描述

n 个点, m 条边,每条边 e 有一个流量下界 lower ( e ) \text{lower}(e) lower(e)和流量上界 upper ( e ) \text{upper}(e) upper(e),给定源点 s 与汇点 t,求源点到汇点的最大流

输出格式

如果无解,输出一行 please go home to sleep
否则输出最大流


有了源汇点之后,不同之处在于源汇点是没有流量平衡的
所以在上一题建图的基础上,再建边 t t t s s s下界为0,上界为inf

注意区分此题中的超级源(ss)汇(tt)和给定源(s)汇(t)
构造网络之后先跑 s s ss ss t t tt tt的最大流,以判断是否存在可行方案

此时的网络只是得到了一种满足流量平衡和流量限制的可行方案, s s s t t t并不一定是最大流
所以还要再跑一次 s s s t t t的最大流

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long lt;

int read()
{
    int f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return f*x;
}

const int inf=1e9;
const int maxn=100010;
int n,m;
struct node{int v,f,nxt;}E[maxn<<1];
int head[maxn],tot=1;
int deg[maxn];
int lev[maxn],sum;

void add(int u,int v,int f)
{
	E[++tot].nxt=head[u];
	E[tot].v=v; E[tot].f=f;
	head[u]=tot; 
}

int bfs(int s,int t)
{
    queue<int> q; q.push(s);
    memset(lev,-1,sizeof(lev)); lev[s]=0;
 
    while(!q.empty())
    {
        int u=q.front(); q.pop();
        for(int i=head[u];i;i=E[i].nxt)
        {
            int v=E[i].v;
            if(lev[v]==-1&&E[i].f)
            {
                lev[v]=lev[u]+1;
                if(v==t) return 1;
                q.push(v);
            }
        }
    }
    return 0;
}
 
int dfs(int u,int cap,int t)
{
    if(u==t) return cap;
    int flow=cap;
    for(int i=head[u];i;i=E[i].nxt)
    {
        int v=E[i].v;
        if( lev[v]==lev[u]+1&&flow&&E[i].f>0)
        {
            int f=dfs(v,min(flow,E[i].f),t);
            E[i].f-=f; E[i^1].f+=f; 
            flow-=f;
        }
    }
    return cap-flow;
}

int dicnic(int s,int t)
{
	int maxf=0;
	while(bfs(s,t)) maxf+=dfs(s,inf,t);
	return maxf;
}

int main()
{
	n=read();m=read(); int s=read(),t=read();
	for(int i=1;i<=m;++i)
	{
		int u=read(),v=read(),l=read(),r=read();
		add(u,v,r-l); add(v,u,0);
		deg[u]-=l; deg[v]+=l;
	}
	add(t,s,inf); add(s,t,0);
	
	int ss=0,tt=n+1;
	for(int i=1;i<=n;++i)
	{
		if(deg[i]>0) add(ss,i,deg[i]),add(i,ss,0),sum+=deg[i];
		else if(deg[i]<0) add(i,tt,-deg[i]),add(tt,i,0);
	}
	
	if(sum!=dicnic(ss,tt)) printf("please go home to sleep");
	else printf("%d",dicnic(s,t));
	return 0;
}

有源汇有上下界最小流

LOJ#117 有源汇有上下界最小流
题目描述

n 个点, m 条边,每条边 e 有一个流量下界 lower ( e ) \text{lower}(e) lower(e)和流量上界 upper ( e ) \text{upper}(e) upper(e),给定源点 s 与汇点 t,求源点到汇点的最小流

输出格式

如果无解,输出一行 please go home to sleep
否则输出最大流


和上以题刚好相反,跑完 s s ss ss t t tt tt的最大流后得到可行流
上一题要把没满的补满,而这一题要把多的去掉

得到可行流后,设当前 t t t s s s的流量大小为 f f f
去掉 t t t s s s的边 t t t s s s的最大流
答案为 f − m a x f ( t , s ) f-maxf(t,s) fmaxf(t,s)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long lt;

int read()
{
    int f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return f*x;
}

const int inf=1e9;
const int maxn=500010;
int n,m;
struct node{int v,f,nxt;}E[maxn<<1];
int head[maxn],tot=1;
int deg[maxn];
int lev[maxn],cur[maxn<<1],sum;

void add(int u,int v,int f)
{
	E[++tot].nxt=head[u];
	E[tot].v=v; E[tot].f=f;
	head[u]=tot; 
}

bool bfs(int s,int t)
{
    queue<int> q; q.push(s);
    memset(lev,-1,sizeof(lev)); lev[s]=0;
    while(!q.empty())
    {
        int u=q.front(); q.pop();
        for(int i=head[u];i;i=E[i].nxt)
        {
            int v=E[i].v;
            if(lev[v]==-1&&E[i].f)
            {
                lev[v]=lev[u]+1;
                if(v==t) return true;
                q.push(v);
            } 
        }
    }
    return false;
}

int dfs(int u,int cap,int t)
{
    if(u==t||!cap) return cap;
    int flow=0;
    for(int i=cur[u];i;i=E[i].nxt)
    {
        int v=E[i].v;
        if(lev[v]==lev[u]+1&&E[i].f)
        {
            int f=dfs(v,min(E[i].f,cap-flow),t);
            E[i].f-=f; E[i^1].f+=f;
            flow+=f;
            if(f) cur[u]=i;
            if(flow==cap) return cap;
        } 
    }
    if(!flow) lev[u]=-1;
    return flow;
}

int dicnic(int s,int t)
{
	int maxf=0;
	while(bfs(s,t))
    {
        for(int i=0;i<=n+m+1;++i) cur[i]=head[i];
        maxf+=dfs(s,inf,t);
    }
	return maxf;
}

int main()
{
	n=read();m=read(); int s=read(),t=read();
	for(int i=1;i<=m;++i)
	{
		int u=read(),v=read(),l=read(),r=read();
		add(u,v,r-l); add(v,u,0);
		deg[u]-=l; deg[v]+=l;
	}
	
	int ss=0,tt=n+1;
	for(int i=1;i<=n;++i)
	{
		if(deg[i]>0) add(ss,i,deg[i]),add(i,ss,0),sum+=deg[i];
		else if(deg[i]<0) add(i,tt,-deg[i]),add(tt,i,0);
	}
	add(t,s,inf); add(s,t,0);
	
	if(sum!=dicnic(ss,tt)) printf("please go home to sleep");
	else {
		head[t]=E[head[t]].nxt;
		head[s]=E[head[s]].nxt;
		int ans=E[tot].f;
		printf("%d",ans-dicnic(t,s));
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值