【知识点】上下界网络流

无源汇有上下界可行流:

问题:给定一张图,每条边流量必须在$[l,r]$之间,请你求出它的一个合法流。

考虑一种初始的想法:将每条边拆成两条,一条流量为$l$,一条流量为$r-l$。

Q:我们只考虑$r-l$的边建一个跟原图一样的图,跑一个合法流,最后加上所有的$l$不就行了吗?

A:显然不行,因为所有$l$并不相等,所以你加上$l$之后原本满足流量平衡的图就不满足流量平衡了。(而且无源汇无上下界跑出来是0啊

但我们可以发现,相对于原图来说,所有必须流的流量$l$是凭空产生,而又凭空被吸收的。

而朴素的网络流问题也正是基于一个凭空产生流量的源点,一个凭空吸收流量的汇点来实现的。

于是处理流量平衡的问题我们可以这样做:

新建一个超级源向每个点连边,表示进入该点的流量至少是多少;新建一个超级汇,每个点向它连边,表示离开该点的流量至少是多少。

然后跑一个最大流,如果满流说明在流量平衡的条件下可以满足要求,否则说明不能满足要求。

更具体地,我们可以:

新建源点$S$,汇点$T$。

对于一条边$(u,v,l,r)$,我们连三条边:$(S,v,l)$,$(u,T,l)$,$(u,v,r-l)$。

边$(S,v,l)$表示进入$v$的流量至少要是$l$。

边$(u,T,l)$表示离开$u$的流量至少要是$l$。

边$(u,v,r-l)$表示流满$l$后的额外流量无下界,上界是$r-l$。

然后我们从$S$到$T$跑最大流。

跑完后检查这张图,当且仅当图中任意一点$u$均满足其与$S,T$的连边均满流时整张图存在合法流。

(若某一点不满流且整张图是最大流,那么无论如何调整一定会有一个点不满流,此时原图上该点的边一定存在流量小于$l$的情况)

(无须检查$>r$的条件,因为若$>r$不合法则一定是因为它造成了某个点$<l$才不合法的)

这个判断条件可以简化成最大流=$S$所有出边的流量之和=$T$所有入边的流量之和。

若存在合法流直接输出此时每条$(u,v,r-l)$边的流量$+l$即可。

#include<bits/stdc++.h>
#define maxn 205
#define maxm 500005
#define inf 0x7fffffff
#define ll long long
#define debug(x) cerr<<#x<<": "<<x<<endl
#define fgx cerr<<"--------------"<<endl
#define dgx cerr<<"=============="<<endl

using namespace std;
int hd[maxn],lim[maxm],to[maxm],nxt[maxm],fl[maxm];
int S,T,cnt=1,id[maxm],dis[maxn],inq[maxn];

inline int read(){
    int x=0,f=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}

inline void addedge(int u,int v,int w){
    to[++cnt]=v,fl[cnt]=w,nxt[cnt]=hd[u],hd[u]=cnt;
    to[++cnt]=u,fl[cnt]=0,nxt[cnt]=hd[v],hd[v]=cnt;
}

inline bool spfa(){
    memset(dis,63,sizeof(dis));
    memset(inq,0,sizeof(inq));
    queue<int> q;q.push(S);
    dis[S]=0,inq[S]=1;
    while(!q.empty()){
        int u=q.front();
        q.pop(),inq[u]=0;
        for(int i=hd[u];i;i=nxt[i]){
            int v=to[i],w=fl[i];
            if(w>0&&dis[v]>dis[u]+1){
                dis[v]=dis[u]+1;
                if(!inq[v]) q.push(v),inq[v]=1;
            }
        }
    }
    return dis[T]!=dis[T+1];
}
inline int dfs(int u,int flow){
    int sum=0;
    if(u==T) return flow;
    for(int i=hd[u];i;i=nxt[i]){
        int v=to[i];
        if(dis[v]==dis[u]+1 && fl[i]>0){
            int f=dfs(v,min(flow,fl[i]));
            sum+=f,fl[i]-=f,fl[i^1]+=f,flow-=f;
        }
        if(!flow) break;
    }
    if(sum==0) dis[u]=-1;
    return sum;
}
inline int Dinic(){int ans=0;while(spfa())ans+=dfs(S,inf);return ans;}

int main(){
    int n=read(),m=read();S=0,T=n+1;
    for(int i=1;i<=m;i++){
        int u=read(),v=read();
        lim[i]=read(); int r=read();
        addedge(S,v,lim[i]),addedge(u,T,lim[i]);
        addedge(u,v,r-lim[i]),id[i]=cnt;
    }
    Dinic(); int flag=1;
    for(int i=hd[S];i;i=nxt[i]) if(fl[i]!=0) flag=0;
    for(int i=hd[T];i;i=nxt[i]) if(fl[i^1]!=0) flag=0;
    if(!flag) printf("NO
");
    else{
        printf("YES
");
        for(int i=1;i<=m;i++)
            printf("%d
",fl[id[i]]+lim[i]);
    }
    return 0;
}
loj115

有源汇有上下界最大流:

当这张图给定源点$S'$,汇点$T'$的时候,它不再满足流量平衡,我们好像没法处理了。

然而根据化归思想,我们从$T'$往$S'$连一条$(0,inf)$的边,这张图就满足流量平衡了……

但我们按上面的方法跑出来不一定是原图的最大流,怎么办呢?

考虑一个想法:在残量网络上再跑一遍$S'$到$T'$的最大流,如何?

注意到跑出来的残量网络在新图上一定是流量平衡的,那么我们跑一遍最大流不会影响除了$S',T'$之外所有点的流量平衡。

而且,因为我们只会沿着原图上的边跑而不会走到$S$或$T$,所以最后跑出来的流一定还是合法的(根本没影响合法的判定条件)。

于是跑两遍最大流就可以解决这个问题了。

第二遍跑的时候不用考虑第一遍的答案,因为第一遍跑完$S'$点流量平衡,相当于初始流量为0。

#include<bits/stdc++.h>
#define maxn 205
#define maxm 500005
#define inf 0x7fffffff
#define ll long long
#define debug(x) cerr<<#x<<": "<<x<<endl
#define fgx cerr<<"--------------"<<endl
#define dgx cerr<<"=============="<<endl

using namespace std;
int hd[maxn],lim[maxm],to[maxm],nxt[maxm],fl[maxm];
int S,T,cnt=1,id[maxm],dis[maxn],inq[maxn];

inline int read(){
    int x=0,f=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}

inline void addedge(int u,int v,int w){
    to[++cnt]=v,fl[cnt]=w,nxt[cnt]=hd[u],hd[u]=cnt;
    to[++cnt]=u,fl[cnt]=0,nxt[cnt]=hd[v],hd[v]=cnt;
}

inline bool spfa(){
    memset(dis,63,sizeof(dis));
    memset(inq,0,sizeof(inq));
    queue<int> q;q.push(S);
    dis[S]=0,inq[S]=1;
    while(!q.empty()){
        int u=q.front();
        q.pop(),inq[u]=0;
        for(int i=hd[u];i;i=nxt[i]){
            int v=to[i],w=fl[i];
            if(w>0&&dis[v]>dis[u]+1){
                dis[v]=dis[u]+1;
                if(!inq[v]) q.push(v),inq[v]=1;
            }
        }
    }
    return dis[T]!=dis[T+1];
}
inline int dfs(int u,int flow){
    int sum=0;
    if(u==T) return flow;
    for(int i=hd[u];i;i=nxt[i]){
        int v=to[i];
        if(dis[v]==dis[u]+1 && fl[i]>0){
            int f=dfs(v,min(flow,fl[i]));
            sum+=f,fl[i]-=f,fl[i^1]+=f,flow-=f;
        }
        if(!flow) break;
    }
    if(sum==0) dis[u]=-1;
    return sum;
}
inline int Dinic(){int ans=0;while(spfa())ans+=dfs(S,inf);return ans;}

int main(){
    int n=read(),m=read(),s=read(),t=read(); 
    S=0,T=n+1; int sum=0,u,v,r;
    for(int i=1;i<=m+1;i++){
        if(i==m+1) u=t,v=s,lim[i]=0,r=inf;
        else u=read(),v=read(),lim[i]=read(),r=read();
        addedge(S,v,lim[i]),addedge(u,T,lim[i]),addedge(u,v,r-lim[i]),id[i]=cnt,sum+=lim[i]; 
    }
    int mx=Dinic(); 
    if(sum!=mx) printf("please go home to sleep
");
    else S=s,T=t,printf("%d
",Dinic());
    return 0;
}
loj116
原文地址:https://www.cnblogs.com/YSFAC/p/12057346.html