zoukankan      html  css  js  c++  java
  • 上下界网络流模型常见解法

    一、无源汇可行流

    1、设立虚拟源汇S、T,IN[i]记录i点流入下限的总和,OUT[i]记录i点流出下限总和

    2、两点间连容量为上限-下限的边

    3、sum=0,遍历所有点i,f=IN[i]-OUT[i]。如果f>0,sum+=f,add(S,i,f);否则,add(i,T,-f)

    4、如果S到T的最大流等于sum,则存在可行流,反之不存在

    例题

    http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2314

    n个点,m条边,每条边从a到b有一个流量下限和一个流量上限,问是否存在可行流,如果存在,输出每边流量

    #include <iostream> 
    #include <queue>
    #include <cstdio>
    #include <cstring>
    using namespace std ;
    
    const int INF=0xfffffff ;
    struct node
    {
        int s,t,cap,nxt ;
    }e[400005] ;
    int m,n,cnt,head[100005],level[100005],q[100005] ;
    void add(int s,int t,int cap)
    {
        e[cnt].s=s ;e[cnt].t=t ;e[cnt].cap=cap ;e[cnt].nxt=head[s] ;head[s]=cnt++ ;
        e[cnt].s=t ;e[cnt].t=s ;e[cnt].cap=0 ;e[cnt].nxt=head[t] ;head[t]=cnt++ ;
    }
    bool build(int s,int t)
    {
        int front=0,rear=0 ;
        memset(level,-1,sizeof(level)) ;
        q[rear++]=s ;
        level[s]=1 ;
        while(front<rear)
        {
            int u=q[front++] ;
            for(int i=head[u] ;i!=-1 ;i=e[i].nxt)
            {
                int tt=e[i].t ;
                if(level[tt]==-1 && e[i].cap>0)
                {
                    level[tt]=level[u]+1 ;
                    if(tt==t)return true ;
                    q[rear++]=tt ;
                }
            }
        }
        return false ;
    }
    int find(int s,int t,int flow)
    {
        if(s==t)return flow ;
        int ret=0,a ;
        for(int i=head[s] ;i!=-1 ;i=e[i].nxt)
        {
            int tt=e[i].t ;
            if(level[tt]==level[s]+1 && e[i].cap>0)
            {
                a=find(tt,t,min(e[i].cap,flow-ret)) ;
                e[i].cap-=a ;
                e[i^1].cap+=a ;
                ret+=a ;
                if(ret==flow)
                    return ret ;
            }
        }
        if(!ret)level[s]=-1 ;
        return ret ;
    }
    int dinic(int s,int t)
    {
        int flow,ret=0 ;
        while(build(s,t))
            while(flow=find(s,t,INF))
                ret+=flow ;
        return ret ;
    }
    int l[100005] ;
    int IN[205],OUT[205] ;
    int C[100005] ;
    int main()
    {
        int t ;
        scanf("%d",&t) ;
        while(t--)
        {
            scanf("%d%d",&n,&m) ;
            cnt=0 ;
            memset(head,-1,sizeof(head)) ;
            memset(IN,0,sizeof(IN)) ;
            memset(OUT,0,sizeof(OUT)) ;
            for(int i=0 ;i<m ;i++)
            {
                int a,b,w ;
                scanf("%d%d%d%d",&a,&b,&l[i],&w) ;
                IN[b]+=l[i] ;OUT[a]+=l[i] ;
                C[i]=w ;
                add(a,b,w-l[i]) ;
            }
            int S,T ;
            S=0 ;T=n+1 ;
            int sum=0 ;
            for(int i=1 ;i<=n ;i++)
            {
                int f=IN[i]-OUT[i] ;
                if(f>0)
                {
                    add(S,i,f) ;
                    sum+=f ;
                }
                else
                    add(i,T,-f) ;
            }
            if(dinic(S,T)==sum)
            {
                puts("YES") ;
                for(int i=0 ;i<m ;i++)
                {
                    printf("%d
    ",C[i]-e[i*2].cap) ;
                }
            }
            else puts("NO") ;
        }
        return 0 ;
    }
    View Code

    二、有源汇可行最大流

    1、源汇s、t,先转化为无源汇可行流判断可行性,建图和判断方法和上面一样,只是多加一条边add(t,s,INF)

    2、如果有解,答案的最大流等于在上图的残留网络中s到t再做一次最大流得到的结果

    例题

    http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=3442

    源点到n个点的流量上限是D,m个点到汇点的下限是G,n和m之间有一些边,容量范围是[L,R]。求最大流。

    #include <iostream> 
    #include <queue>
    #include <cstdio>
    #include <cstring>
    #include <vector>
    using namespace std ;
    
    const int INF=0xfffffff ;
    struct node
    {
        int s,t,cap,nxt ;
    }e[400005] ;
    int m,n,cnt,head[100005],level[100005],q[100005] ;
    void add(int s,int t,int cap)
    {
        e[cnt].s=s ;e[cnt].t=t ;e[cnt].cap=cap ;e[cnt].nxt=head[s] ;head[s]=cnt++ ;
        e[cnt].s=t ;e[cnt].t=s ;e[cnt].cap=0 ;e[cnt].nxt=head[t] ;head[t]=cnt++ ;
    }
    bool build(int s,int t)
    {
        int front=0,rear=0 ;
        memset(level,-1,sizeof(level)) ;
        q[rear++]=s ;
        level[s]=1 ;
        while(front<rear)
        {
            int u=q[front++] ;
            for(int i=head[u] ;i!=-1 ;i=e[i].nxt)
            {
                int tt=e[i].t ;
                if(level[tt]==-1 && e[i].cap>0)
                {
                    level[tt]=level[u]+1 ;
                    if(tt==t)return true ;
                    q[rear++]=tt ;
                }
            }
        }
        return false ;
    }
    int find(int s,int t,int flow)
    {
        if(s==t)return flow ;
        int ret=0,a ;
        for(int i=head[s] ;i!=-1 ;i=e[i].nxt)
        {
            int tt=e[i].t ;
            if(level[tt]==level[s]+1 && e[i].cap>0)
            {
                a=find(tt,t,min(e[i].cap,flow-ret)) ;
                e[i].cap-=a ;
                e[i^1].cap+=a ;
                ret+=a ;
                if(ret==flow)
                    return ret ;
            }
        }
        if(!ret)level[s]=-1 ;
        return ret ;
    }
    int dinic(int s,int t)
    {
        int flow,ret=0 ;
        while(build(s,t))
            while(flow=find(s,t,INF))
                ret+=flow ;
        return ret ;
    }
    int IN[4005],OUT[4005] ;
    vector <int> vis[4005] ;
    vector <int> aa[4005] ;
    int main()
    {
        while(~scanf("%d%d",&n,&m))
        {
            cnt=0 ;
            memset(head,-1,sizeof(head)) ;
            memset(IN,0,sizeof(IN)) ;
            memset(OUT,0,sizeof(OUT)) ;
            int s,t ;
            s=0 ;t=n+m+1 ;
            for(int i=1 ;i<=m ;i++)
            {
                int G ;
                scanf("%d",&G) ;
                add(n+i,t,INF-G) ;
                OUT[n+i]+=G ;IN[t]+=G ;
            }
            for(int i=0 ;i<4005 ;i++)
            {
                vis[i].clear() ;
                aa[i].clear() ;
            }
            for(int i=1 ;i<=n ;i++)
            {
                int C,D ;
                scanf("%d%d",&C,&D) ;
                add(s,i,D) ;
                for(int j=0 ;j<C ;j++)
                {
                    int a,L,R ;
                    scanf("%d%d%d",&a,&L,&R) ;
                    add(i,a+n+1,R-L) ;
                    vis[i].push_back(cnt-1) ;
                    aa[i].push_back(L) ;
                    OUT[i]+=L ;IN[a+n+1]+=L ;
                }
            }
            add(t,s,INF) ;
            int S,T ;
            S=t+1 ;T=S+1 ;
            int sum=0 ;
            for(int i=0 ;i<=t ;i++)
            {
                int f=IN[i]-OUT[i] ;
                if(f>0)
                {
                    add(S,i,f) ;
                    sum+=f ;
                }
                else
                    add(i,T,-f) ;
            }
            if(dinic(S,T)==sum)
            {
                int ans=dinic(s,t) ;
                printf("%d
    ",ans) ;
                for(int i=1 ;i<=n ;i++)
                {
                    for(int j=0 ;j<vis[i].size() ;j++)
                        printf("%d
    ",e[vis[i][j]].cap+aa[i][j]) ;
                }
            }
            else puts("-1") ;
            putchar('
    ') ;
        }
        return 0 ;
    }
    View Code

    三、有源汇可行最小流

    1、源汇s、t,先转化为无源汇可行流判断可行性,建图方法和上面一样

    2、对S、T求最大流f1,之后add(t,s,INF)

    3、再次对S、T求最大流f2,如果f1+f2等于sum,则有解,最小流等于t到s的流量

    例题

    http://acm.hdu.edu.cn/showproblem.php?pid=3157

    n个接线柱,还有两个正负极,m条边,两个元件之间有一个正常工作的电流下限,求整个电路都正常工作的最小电流。

    显然正极是源点,负极是汇点,开搞

    #include <iostream> 
    #include <queue>
    #include <cstdio>
    #include <cstring>
    #include <vector>
    using namespace std ;
    
    const int INF=0xfffffff ;
    struct node
    {
        int s,t,cap,nxt ;
    }e[400005] ;
    int m,n,cnt,head[100005],level[100005],q[100005] ;
    void add(int s,int t,int cap)
    {
        e[cnt].s=s ;e[cnt].t=t ;e[cnt].cap=cap ;e[cnt].nxt=head[s] ;head[s]=cnt++ ;
        e[cnt].s=t ;e[cnt].t=s ;e[cnt].cap=0 ;e[cnt].nxt=head[t] ;head[t]=cnt++ ;
    }
    bool build(int s,int t)
    {
        int front=0,rear=0 ;
        memset(level,-1,sizeof(level)) ;
        q[rear++]=s ;
        level[s]=1 ;
        while(front<rear)
        {
            int u=q[front++] ;
            for(int i=head[u] ;i!=-1 ;i=e[i].nxt)
            {
                int tt=e[i].t ;
                if(level[tt]==-1 && e[i].cap>0)
                {
                    level[tt]=level[u]+1 ;
                    if(tt==t)return true ;
                    q[rear++]=tt ;
                }
            }
        }
        return false ;
    }
    int find(int s,int t,int flow)
    {
        if(s==t)return flow ;
        int ret=0,a ;
        for(int i=head[s] ;i!=-1 ;i=e[i].nxt)
        {
            int tt=e[i].t ;
            if(level[tt]==level[s]+1 && e[i].cap>0)
            {
                a=find(tt,t,min(e[i].cap,flow-ret)) ;
                e[i].cap-=a ;
                e[i^1].cap+=a ;
                ret+=a ;
                if(ret==flow)
                    return ret ;
            }
        }
        if(!ret)level[s]=-1 ;
        return ret ;
    }
    int dinic(int s,int t)
    {
        int flow,ret=0 ;
        while(build(s,t))
            while(flow=find(s,t,INF))
                ret+=flow ;
        return ret ;
    }
    int IN[4005],OUT[4005] ;
    int main()
    {
        while(~scanf("%d%d",&n,&m))
        {
            if(!n && !m)break ;
            cnt=0 ;
            memset(head,-1,sizeof(head)) ;
            memset(IN,0,sizeof(IN)) ;
            memset(OUT,0,sizeof(OUT)) ;
            int s,t ;
            s=0 ;t=n+1 ;
            for(int i=1 ;i<=m ;i++)
            {
                int a,b,w ;
                char s1[15],s2[15] ;
                scanf("%s%s%d",s1,s2,&w) ;
                if(s1[0]=='+')a=s ;
                else sscanf(s1,"%d",&a) ;
                if(s2[0]=='-')b=t ;
                else sscanf(s2,"%d",&b) ;
                add(a,b,INF-w) ;
                OUT[a]+=w ;IN[b]+=w ;
            }
            int S,T ;
            S=t+1 ;T=S+1 ;
            int sum=0 ;
            for(int i=0 ;i<=t ;i++)
            {
                int f=IN[i]-OUT[i] ;
                if(f>0)
                {
                    add(S,i,f) ;
                    sum+=f ;
                }
                else
                    add(i,T,-f) ;
            }
            int f1=dinic(S,T) ;
            int gao=cnt ;
            add(t,s,INF) ;
            int f2=dinic(S,T) ;
            if(f1+f2==sum)
            {
                printf("%d
    ",e[gao+1].cap) ;
            }
            else puts("impossible") ;
        }
        return 0 ;
    }
    View Code
  • 相关阅读:
    Building a flexiable renderer
    Indirect Illumination in mental ray
    我的心情
    Cellular Automata
    Subsurface Scattering in mental ray
    Shader Types in mental ray
    BSP Traversal
    我的渲染器终于达到了MR的速度
    How to handle displacement and motion blur
    说明
  • 原文地址:https://www.cnblogs.com/xiaohongmao/p/4003711.html
Copyright © 2011-2022 走看看