P14580 【模板】有源汇上下界最小流 题解

前置知识

  1. 无源汇上下界可行流 (可以参考我的另一篇题解 )。

  2. 网络流基本概念。

题目传送门

由于有源汇上下界最大流和最小流原理相近,本文一起介绍,加深理解。主要以有源汇上下界最大流为例。

算法介绍

先说具体流程

\(S,T\) 为超级源汇点 (用来跑无源汇上下界可行流)。

  1. \(t\)\(s\) 容量为 \(+\infty\) 的边,跑无源汇上下界可行流,记流量 \(ans\)\((t,s,+\infty)\) 这条边反向边的流量。注意!不能记最大流的结果,此处 \(ans\)\(s\)\(t\) 的流量,而最大流跑的是 \(S\)\(T\) 的最大流

  2. \(t\)\(s\) 的边和其反向边容量改为 \(0\),即删除此边。再跑从 \(s\)\(t\) 的最大流,累加到 \(ans\) 上即为答案。

正确性证明

以下内容为本人学习过程中结合多处资料,仔细思考后所总结的一种容易理解的解释方法,将详细解释每一步的缘由,同时排除一些误区,以及其他题解可能疏漏的细节。

初步理解:由于出现了源点和汇点两个流量不守恒的点,所以需要做出转化,同时最大 / 小流的前提是存在可行流。

解析

尝试先用无源汇的思路切入。

考虑源点 \(s\) 和汇点 \(t\) 的联系,由于 \(s\) 的流出流量最终都流入 \(t\),所以直接从 \(t\)\(s\) 连一条容量为 \(+\infty\) 的边就可以把整张图转化为无源汇的图。

进一步,考虑先用无源汇上下界可行流的做法求出一条可行流 (前提是有,不然无解),得到它的残留网了,再考虑最大化。

记新建的超级源汇为 \(S,T\)

那么由于此残留网络为对应是可行流,所以与 \(S,T\) 相连的边都满流了 (参考无源汇)。

此时要最大化,就是使 \(s\)\(t\) 的流量尽可能增多,其实只要找到 \(s\)\(t\) 的所有增广路即可,这正是最大流干的事,考虑再从 \(s\)\(t\) 跑一遍最大流。

最后,还要考虑一开始加的 \(t\)\(s\)\(+\infty\) 边,由于这条边的残余网络上可能有流量,所以会直接导致流量被退回使最大流增加,所以跑最大流之前需删除此边。


另外,还有很重要的一点,为什们可以在残余网络上换源汇点跑最大流,还不影响可行流的性质呢?此处不能单纯的认为下界已经锁死就满足原性质。究其本质,其实就是我们在做无源汇时新加的边满流,使得可行流能够满足下界的限制。

也就是说,只要能保持满流,最大流的叠加就不会有影响。我们发现,\(S,T\) 都只连出边或入边,如果最大流找增广路搜索到了 \(S,T\),就会因为它们所连的边和其反向边有一条是没有容量的,则出不来或进不去,就不会产生退流,即保持满流。

最小流:

与最大流相反改跑最小流,在可行流的基础上,尽可能多的从 \(t\)\(s\) 退流,即跑 \(t\)\(s\) 的最大流,从 \(ans\) 上减去即可。

#include <bits/stdc++.h>
using namespace std;

inline int read(){
    int d=0,f=0;char ch=getchar();
    while (!isdigit(ch)) f|=(ch=='-'),ch=getchar(); 
    while (isdigit(ch)) d=d*10+ch-'0',ch=getchar();
    return f?-d:d;
}

const int N=1005,M=N+10005,inf=2e9+7;

int n,m,h[N],idx,S,T,s,t,w[N];

struct Edge{int t,c,ne;}e[M<<1];
inline void add(int a,int b,int c){
    e[idx]={b,c,h[a]};h[a]=idx++;
    e[idx]={a,0,h[b]};h[b]=idx++;
}

int q[N],hh,tt,d[N],cur[N];

inline bool bfs(){
    memset(d,0,sizeof(int)*(n+2));
    hh=0,tt=-1,q[++tt]=S,d[S]=1;
    while (hh<=tt){
        int u=q[hh++];
        for (int i=h[u];~i;i=e[i].ne){
            int v=e[i].t;
            if (!d[v]&&e[i].c){
                d[v]=d[u]+1,q[++tt]=v;
                if (v==T) return true;
            }
        }
    }
    return false;
}

int dfs(int u,int mf){
    if (u==T) return mf;
    int sum=0;
    for (int &i=cur[u];~i;i=e[i].ne){
        int v=e[i].t;
        if (d[v]==d[u]+1&&e[i].c){
            int f=dfs(v,min(e[i].c,mf));
            mf-=f,sum+=f;
            e[i].c-=f,e[i^1].c+=f;
            if (!mf) break;
        } 
    }
    if (!sum) d[u]=0; 
    return sum;
}

inline int Dinic(){
    int flow=0;
    while (bfs()){
        memcpy(cur,h,sizeof(int)*(n+2));
        flow+=dfs(S,inf);
    }
    return flow;
}

int main(){
    n=read(),m=read(),s=read(),t=read(),S=0,T=n+1;

    memset(h,-1,sizeof(int)*(n+2));
    
    for (int i=1;i<=m;i++){
        int u=read(),v=read(),l=read(),r=read();
        add(u,v,r-l);
        w[u]-=l,w[v]+=l;
    }

    int sum=0;
    for (int i=1;i<=n;i++)
        if (w[i]>0) add(S,i,w[i]),sum+=w[i];
        else add(i,T,-w[i]);

    add(t,s,inf);//转化为无源汇
    int ans=Dinic();
    if (ans<sum) puts("N");
    else {
        int res=e[idx-1].c;
        e[idx-1].c=e[idx-2].c=0;//删边
        T=s,S=t;//注意,T=s,S=t
        printf("%d\n",res-Dinic());
    }
    return 0;
}
posted @ 2026-03-24 12:32  TP2010  阅读(5)  评论(0)    收藏  举报