zoukankan      html  css  js  c++  java
  • 【知识点】上下界网络流

    无源汇有上下界可行流:

    问题:给定一张图,每条边流量必须在$[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
  • 相关阅读:
    剑指 Offer 18. 删除链表的节点
    剑指 Offer 15. 二进制中1的个数
    剑指 Offer 11. 旋转数组的最小数字
    剑指 Offer 56
    剑指 Offer 10- II. 青蛙跳台阶问题
    剑指 Offer 10- I. 斐波那契数列
    剑指 Offer 09. 用两个栈实现队列
    剑指 Offer 06. 从尾到头打印链表
    C++ 异常机制
    读《大数据——互联网大规模数据挖掘与分布式处理》
  • 原文地址:https://www.cnblogs.com/YSFAC/p/12057346.html
Copyright © 2011-2022 走看看