【模板】最大流之上下界可行流

ACM模板


无源汇上下界可行流

问题: 给定一个网络,求一个流满足:每条边的流量处在给定的下界和上届[lower,upper]之间,满足流量守恒

首先我们在原网络中确定一个全部是下界的流网络,当然此流不一定是可行流因为其不一定满足流量守恒,不妨叫此流流①
此时问题转化成找到一个流②,每条边的流量处在[0,upper-lower]之间,此流与之前的流①相加(流①+流②)满足流量守恒,是一个可行流。显然流②不一定是可行流,但是我们可以经过以下调整将其变成一个可行流,从而能过求出流②

  • A ( u ) = ∑ t o i = u f ( i ) − ∑ f r o m i = u f ( i ) A(u)=\sum_{toi=u}f(i)-\sum_{fromi=u}f(i) A(u)=toi=uf(i)fromi=uf(i)即流入减去流出
  • A ( u ) > 0 A(u)>0 A(u)>0,源点 S S S u u u点连边,容量是 A ( u ) A(u) A(u)
  • A ( u ) < 0 A(u)<0 A(u)<0 u u u点向汇点 T T T连边,容量是 − A ( u ) -A(u) A(u)

如果满流此时即可求出流②(否则无解),再加上流①即是满足题意的可行流
无源汇上下界可行流

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=210,M=(10310+N)*2;
int n,m,S,T;
int h[N],e[M],ne[M],l[M],f[M],idx;
int d[N],q[N],A[N],cur[N];
void add(int a,int b,int c,int d)// 下界c 上届d
{                                        //建图上界-下界
    e[idx]=b,ne[idx]=h[a],l[idx]=c,f[idx]=d-c,h[a]=idx++;
    e[idx]=a,ne[idx]=h[b],f[idx]=0,h[b]=idx++;
}
bool bfs()
{
    memset(d,-1,sizeof d);
    int tt=0,hh=0;
    d[S]=0,q[0]=S,cur[S]=h[S];
    while(hh<=tt)
    {
        int t=q[hh++];
        for(int i=h[t];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(d[j]==-1&&f[i])
            {
                d[j]=d[t]+1;
                cur[j]=h[j];
                if(j==T) return 1;
                q[++tt]=j;
            }
        }
    }
    return 0;
}
int dfs(int u=S,int flow=0x3f3f3f3f)
{
    if(u==T) return flow;
    int rmn=flow;
    for(int i=cur[u];i!=-1&&rmn;i=ne[i])
    {
        cur[u]=i;
        int j=e[i];
        if(d[j]==d[u]+1&&f[i])
        {
            int t=dfs(j,min(f[i],rmn));
            if(!t) d[j]=-1;
            f[i]-=t,f[i^1]+=t,rmn-=t;
        }
    }
    return flow-rmn;
}
int dinic()
{
    int r=0;
    while(bfs()) r+=dfs();
    return r;
}
int main()
{
    cin>>n>>m;
    S=0,T=n+1;
    memset(h,-1,sizeof h);
    for(int i=1;i<=m;i++)
    {
        int a,b,c,d;
        cin>>a>>b>>c>>d;
        add(a,b,c,d);
        A[b]+=c,A[a]-=c;
    }
    int tot=0;
    for(int i=1;i<=n;i++)
    {
        if(A[i]>0) 
            add(S,i,0,A[i]),tot+=A[i];
        else if(A[i]<0)
            add(i,T,0,-A[i]);
    }
    if(tot!=dinic())
        cout<<"NO\n";
    else
    {
        cout<<"YES\n";
        for(int i=0;i<2*m;i+=2)
                cout<<f[i^1]+l[i]<<'\n';// 流②+流①
    }
    return 0;
}

有源汇上下界最大流

连接一条t->s下界是0上界是 ∞ ∞ 的边,由此转化循环流问题(无源汇上下界可行流)

按照循环流问题建图,首先跑dinic看是否能够找到一条可行流(即判断是否是满流)
然后在换源点和汇点并删去t->s的边再跑一边dinic(榨干残留网络),可行流+第二次dinic即是最大流

心里没有一点AC数的详细证明

有源汇上下界最大流

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=210,M=(N+10000)*2;
int h[N],e[M],ne[M],f[M],idx;
int n,m,S,T;
int d[N],q[N],cur[N],A[N];
void add(int a,int b,int c)
{
    e[idx]=b,f[idx]=c,ne[idx]=h[a],h[a]=idx++;
    e[idx]=a,f[idx]=0,ne[idx]=h[b],h[b]=idx++;
}
bool bfs()
{
    int tt=0,hh=0;
    memset(d,-1,sizeof d);
    d[S]=0,cur[S]=h[S],q[0]=S;
    while(hh<=tt)
    {
        int t=q[hh++];
        for(int i=h[t];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(d[j]==-1&&f[i])
            {
                d[j]=d[t]+1;
                cur[j]=h[j];
                if(j==T) return 1;
                q[++tt]=j;
            }
        }
    }
    return 0;
}
int dfs(int u=S,int flow=0x3f3f3f3f)
{
    if(u==T) return flow;
    int rmn=flow;
    for(int i=cur[u];i!=-1&&rmn;i=ne[i])
    {
        cur[u]=i;
        int j=e[i];
        if(d[j]==d[u]+1&&f[i])
        {
            int t=dfs(j,min(f[i],rmn));
            if(!t) d[j]=-1;
            f[i]-=t,f[i^1]+=t,rmn-=t;
        }
    }
    return flow-rmn;
}

int dinic()
{
    int r=0;
    while(bfs()) r+=dfs();
    return r;
}
int main()
{
    int s,t;
    cin>>n>>m>>s>>t;
    memset(h,-1,sizeof h);
    S=0,T=n+1;
    for(int i=1;i<=m;i++)
    {
        int a,b,c,d;
        cin>>a>>b>>c>>d;
        add(a,b,d-c);
        A[a]-=c,A[b]+=c;
    }
    int tot=0;
    for(int i=1;i<=n;i++)
    {
        if(A[i]>0)
            add(S,i,A[i]),tot+=A[i];
        else if(A[i]<0)
            add(i,T,-A[i]);
    }
    add(t,s,0x3f3f3f3f);
    if(dinic()!=tot) 
        cout<<"No Solution\n";
    else
    {
        int f1=f[idx-1];
        S=s,T=t;
        f[idx-1]=f[idx-2]=0; //删去 t->s的边
        cout<<f1+dinic()<<'\n';
    }
}

有源汇上下界最小流

最小流=可行流 + + +s->t流
由于可行流固定,为了使结果最小即s->t流最小
由于 s->t的流= - t->s的流
如果t->s求最大流,那么s->t就是最小流, f s → t = − f t → s f_{s\to t}=-f_{t\to s} fst=fts 这也是为什么相减的原因

有源汇上下界最小流

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=50010,M=(N+126000)*2;
int h[N],e[M],ne[M],f[M],idx;
int n,m,S,T;
int d[N],q[N],cur[N],A[N];
void add(int a,int b,int c)
{
    e[idx]=b,f[idx]=c,ne[idx]=h[a],h[a]=idx++;
    e[idx]=a,f[idx]=0,ne[idx]=h[b],h[b]=idx++;
}
bool bfs()
{
    int tt=0,hh=0;
    memset(d,-1,sizeof d);
    d[S]=0,cur[S]=h[S],q[0]=S;
    while(hh<=tt)
    {
        int t=q[hh++];
        for(int i=h[t];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(d[j]==-1&&f[i])
            {
                d[j]=d[t]+1;
                cur[j]=h[j];
                if(j==T) return 1;
                q[++tt]=j;
            }
        }
    }
    return 0;
}
int dfs(int u=S,int flow=0x3f3f3f3f)
{
    if(u==T) return flow;
    int rmn=flow;
    for(int i=cur[u];i!=-1&&rmn;i=ne[i])
    {
        cur[u]=i;
        int j=e[i];
        if(d[j]==d[u]+1&&f[i])
        {
            int t=dfs(j,min(f[i],rmn));
            if(!t) d[j]=-1;
            f[i]-=t,f[i^1]+=t,rmn-=t;
        }
    }
    return flow-rmn;
}

int dinic()
{
    int r=0;
    while(bfs()) r+=dfs();
    return r;
}
int main()
{
    int s,t;
    cin>>n>>m>>s>>t;
    memset(h,-1,sizeof h);
    S=0,T=n+1;
    for(int i=1;i<=m;i++)
    {
        int a,b,c,d;
        cin>>a>>b>>c>>d;
        add(a,b,d-c);
        A[a]-=c,A[b]+=c;
    }
    int tot=0;
    for(int i=1;i<=n;i++)
    {
        if(A[i]>0)
            add(S,i,A[i]),tot+=A[i];
        else if(A[i]<0)
            add(i,T,-A[i]);
    }
    add(t,s,0x3f3f3f3f);
    if(dinic()!=tot) 
        cout<<"No Solution\n";
    else
    {
        int f1=f[idx-1];
        S=t,T=s;
        f[idx-1]=f[idx-2]=0; //删去 t->s的边
        cout<<f1-dinic()<<'\n';
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值