zoukankan      html  css  js  c++  java
  • 【图论】无源汇上下界可行流


    无源汇上下界可行流

    可行流即对于一张无源点与汇点的流量网络(G)

    询问是否存在一种流量方案,使得每条边的流量在对应的上下界内,且每个点流量平衡




    解法

    既然对于流量网络(G)内每一条边的流量都进行了限制,使其必须处于([L_i,R_i])之内

    那么就假设每条边一开始就存在着要求的最小流量(L_i)

    这条边此时的容量便变成了(R_i-L_i),初始流量为(0),以此初步建立流量网络

    这样处理便解决了上下界的限制

    但同时也导致每个点的流量不一定平衡,即流入量≠流出量

    所以接下来要对点进行平衡处理,使得每条边与原图中对应的边相加后,全图流量平衡

    构建虚拟源点(S)与虚拟汇点(T),使得能够虚拟提供流量

    由于上面我们处理时使得每条边都固定拥有了流量(L_i)

    那么对于这条边连接的两个点(u_i,v_i)

    (u_i)的流出量(out[u_i]+=L_i)(v_i)的流入量(in[v_i]+=L_i)

    处理完所有边后,得到每个点的总流入量与总流出量

    可得,如果对于点(i)

    (in[i]>=out[i]):该点流入量较流出量大,说明点(i)需要多向外输出(in[i]-out[i])的流量,那么这些流量就从虚拟源点(S)取,故建立(S)(i)的流量为(in[i]-out[i])的一条边

    (in[i]<out[i]):该点流出量较流入量大,说明外面需要多向点(i)输入(out[i]-in[i])的流量,换言之可以将该点多余的流量流向虚拟汇点(T),故建立(i)(T)的流量为(out[i]-in[i])的一条边



    可行流求解

    如上建立流量网络

    可知,对于与源点/汇点连接的边的容量和代表着全图的流量总和

    令所有(in[i]>=out[i])情况下的(in[i]-out[i])总和为(sum)(或所有(in[i]<out[i])情况下的(out[i]-in[i])总和)

    对于这个流量网络,只有当最大流量流满(sum)时,才能做到全图的流量平衡

    所以直接跑Dinic​,判断最大流是否等于(sum)即可




    例题

    ZOJ 2314 Reactor Cooling

    模板题,多组数据,给定点数与边数,给出每条边连向即上下界,问一种使得流量平衡的可行解

    最后取出跑Dinic后每条边对应的反向边流量作为该边的流量,加上初始的流量下界作为该边最终流量即可

    #include<bits/stdc++.h>
    using namespace std;
    const int INF=0x3f3f3f3f;
    
    const int maxn=200,maxm=40000;
    struct edge{
        int u,v,cap,flow;
        edge(){}
        edge(int u,int v,int cap,int flow):u(u),v(v),cap(cap),flow(flow){}
    }eg[maxm<<1];
    int tot,s,t,dis[maxn<<1],cur[maxn<<1];
    vector<int> tab[maxn<<1];
    
    void init(int n,int s_,int t_){
        while(n)tab[n--].clear();
        tot=0,s=s_,t=t_;
    }
    void addedge(int u,int v,int cap){
        tab[u].push_back(tot);
        eg[tot++]=edge(u,v,cap,0);
        tab[v].push_back(tot);
        eg[tot++]=edge(v,u,0,0);
    }
    int bfs(){
        queue<int> q;
        q.push(s);
        memset(dis,INF,sizeof dis);
        dis[s]=0;
        while(!q.empty()){
            int h=q.front(),i;
            q.pop();
            for(i=0;i<tab[h].size();i++){
                edge &e=eg[tab[h][i]];
                if(e.cap>e.flow&&dis[e.v]==INF){
                    dis[e.v]=dis[h]+1;
                    q.push(e.v);
                }
            }
        }
        return dis[t]<INF;
    }
    int dfs(int x,int maxflow){
        if(x==t|maxflow==0)
            return maxflow;
        int flow=0,i,f;
        for(i=cur[x];i<tab[x].size();i++){
            cur[x]=i;
            edge &e=eg[tab[x][i]];
            if(dis[e.v]==dis[x]+1&&(f=dfs(e.v,min(maxflow,e.cap-e.flow)))>0){
                e.flow+=f;
                eg[tab[x][i]^1].flow-=f;
                flow+=f;
                maxflow-=f;
                if(maxflow==0)
                    break;
            }
        }
        return flow;
    }
    int dinic(){
        int flow=0;
        while(bfs()){
            memset(cur,0,sizeof(cur));
            flow+=dfs(s,INF);
        }
        return flow;
    }
    
    struct node
    {
        int u,v,l,r;
    }E[maxm<<1];
    int in[maxn],out[maxn];
    
    void solve()
    {
        int n,m,u,v,sum=0;
        scanf("%d%d",&n,&m);
        init(n+2,n+1,n+2);
        for(int i=1;i<=n;i++)
            in[i]=out[i]=0;
        
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d%d",&E[i].u,&E[i].v,&E[i].l,&E[i].r);
            in[E[i].v]+=E[i].l;
            out[E[i].u]+=E[i].l;
            addedge(E[i].u,E[i].v,E[i].r-E[i].l); //限制流量为r-l
        }
        for(int i=1;i<=n;i++)
        {
            if(in[i]>=out[i])
                addedge(s,i,in[i]-out[i]),sum+=in[i]-out[i];
            else
                addedge(i,t,out[i]-in[i]);
        }
        
        if(dinic()<sum)
        {
            puts("NO");
            return;
        }
        puts("YES");
        for(int i=1;i<2*m;i+=2)
            printf("%d
    ",E[i/2+1].l+eg[i^1].flow); //从跑出的残量网络中取反向边的flow为当前可行流量,加上原先的下界作为流量
    }
    int main()
    {
        int T;
        scanf("%d",&T);
        while(T--)
            solve();
        return 0;
    }
    

  • 相关阅读:
    Java中的break和continue关键字使用总结
    Java Foreach语句使用总结
    Android的线程(Thread)和服务(Service)的区别
    Android终止无限循环线程和程序退出时销毁线程
    IoC(Inversion of Control,控制反转)模式
    浅谈手机app测试注意点
    Android获取手机相关信息
    深搜
    5种排序方式
    一个简单的文本编辑器
  • 原文地址:https://www.cnblogs.com/stelayuri/p/13621856.html
Copyright © 2011-2022 走看看