码迷,mamicode.com
首页 > 其他好文 > 详细

有上下界的网络流

时间:2017-12-15 23:42:58      阅读:357      评论:0      收藏:0      [点我收藏+]

标签:struct   water   class   math   fill   review   建图   article   span   

转载自http://blog.csdn.net/clove_unique/article/details/54884437

定义

f(u,v)表示u->v这条边的实际流量 
b(u,v)表示u->v这条边的流量下界 
c(u,v)表示u->v这条边的流量上界 
在一个无源汇的普通网络流图中,满足

  • 0f(u,v)c(u,v)
  • f(u,i)=f(i,v)

分别称为流量限制条件和流量平衡条件 
而在有上下界的网络流图中,由于多了流量下界b(u,v)的限制,满足

  • b(u,v)f(u,v)c(u,v)
  • f(u,i)=f(i,v)

无源汇可行流

建图方法

将有上下界的网络流图转化成普通的网络流图

  • 首先建立附加源点ss和附加汇点tt
  • 对于原图中的边x->y,若限制为[b,c],那么连边x->y,流量为c-b
  • 对于原图中的某一个点i,记d(i)为流入这个点的所有边的下界和减去流出这个点的所有边的下界和 
    • 若d(i)>0,那么连边ss->i,流量为d(i)
    • 若d(i)<0,那么连边i->tt,流量为-d(i)

求解方法

在新图上跑ss到tt的最大流 
若新图满流,那么一定存在一种可行流 
此时,原图中每一条边的流量应为新图中对应的边的流量+这条边的流量下界

证明

在原图中,假设每条边的实际流量为f(u,v)=b(u,v)+g(u,v) 
其中g(u,v)c(u,v)b(u,v)

我们可以将原图中的边改为上界为c(u,v)b(u,v)的边,变成一个普通的网络流图 
经过以上的改造,g(u,v)实际上是新图中边u->v的实际流量,原图中的实际流量f(u,v)=b(u,v)+g(u,v) 
但是如果在这个新图中直接求可行流的话是错误的

举个栗子 
原图 
技术分享图片 
按照上面的方法改造过的新图 
技术分享图片 
这个图的可行流为1,还原成原图的实际流量为 
技术分享图片 
很显然满足流量限制条件,但是不满足流量平衡条件

由于需要满足流量平衡条件 
f(u,i)=f(i,v) 
[b(u,i)+g(u,i)]=[b(i,v)+g(i,v)] 
b(u,i)b(i,v)=g(i,v)g(u,i) 
d(i)=b(u,i)b(i,v) 
d(i)>0时 
g(i,v)=g(u,i)+d(i) 
所以需要一条边流量为d(i)来补流 
技术分享图片 
d(i)<0时 
g(u,i)=g(i,v)+[d(i)] 
所以需要一条边流量为d(i)来分流 
技术分享图片 
可以发现,添加的所有与附加源点或者附加汇点相连的边必须满流,原图才有可行流 
证毕

例题ZOJ Problem Set - 2314Reactor Cooling

 

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define maxn 205
#define maxm 50000
struct Edge{
    int next,to,w,fl;
}edge[maxm];
int n,m,t,fi[maxn],flow[maxn],ans[maxm],depth[maxn],que[maxn],first,last;
bool vis[maxn];
inline void add_edge(int u,int v,int w,int x){
    edge[x].next=fi[u],fi[u]=x,edge[x].to=v,edge[x].w=w,edge[x].fl=0,
    edge[x^1].next=fi[v],fi[v]=x^1,edge[x^1].to=u,edge[x^1].w=edge[x^1].fl=0;
} 
inline void init(){
    memset(fi,0,sizeof(fi)),memset(flow,0,sizeof(flow));
}
inline bool bfs(){//查找是否存在增广路
    memset(vis,0,sizeof(vis));
    depth[0]=1,first=last=0,que[last++]=0,vis[0]=1;
    while(first<last){
        int x=que[first++],v;
        for(int i=fi[x];i;i=edge[i].next){
            v=edge[i].to;
            if(!vis[v]&&edge[i].w)que[last++]=v,vis[v]=1,depth[v]=depth[x]+1;//注:流量为0的边不能算 
        }
    }
    return vis[t];
}
int dfs(int u,int f){
    if(u==t||!f)return f;
    int v,x,sum=0;
    for(int i=fi[u];i;i=edge[i].next){
        v=edge[i].to;
        if(depth[v]==depth[u]+1){//增广路必须走向下一层 
            x=dfs(v,min(edge[i].w,f-sum));
            sum+=x,edge[i].w-=x,edge[i^1].w+=x,edge[i].fl+=x,edge[i^1].fl-=x;
        }
    }
    return sum;
}
inline int dinic(){//dinic求最大流 
    int sum=0,x;
    while(bfs()){//将图分层 
        if(x=dfs(0,0x3f3f3f3f))sum+=x;//在分过层的图上进行dfs求增广路 
    }
    return sum;
}
inline void work(){
    init(),scanf("%d%d",&n,&m),t=n+1;
    int u,v,w,se=(m<<1)+1,sum=0;
    for(int i=1;i<=m;i++){
        scanf("%d%d%d%d",&u,&v,ans+i,&w),
        add_edge(u,v,w-ans[i],i<<1),flow[u]+=ans[i],flow[v]-=ans[i];
    }
    for(int i=1;i<=n;i++){
        if(flow[i]>0)add_edge(i,t,flow[i],++++se),sum+=flow[i];//若flow(i)>0,那么连边i->t,流量为flow(i)
        if(flow[i]<0)add_edge(0,i,-flow[i],++++se);//若flow(i)<0,那么连边s->i,流量为-flow(i)
    }
    if(dinic()==sum){//若新图满流,那么一定存在一种可行流  
        printf("YES\n");
        for(int i=1;i<=m;i++){
            printf("%d\n",ans[i]+edge[i<<1].fl);//每一条边的流量应为新图中对应的边的流量+这条边的流量下界
        }
    }
    else printf("NO\n");//否则,不存在可行流 
}
int main(){
    int t;scanf("%d",&t);
    while(t--)work(),printf("\n");
    return 0;
} 

 

有上下界的网络流

标签:struct   water   class   math   fill   review   建图   article   span   

原文地址:http://www.cnblogs.com/bennettz/p/8045036.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!