SGU_176 Flow construction 有上下界的网络流

http://acm.sgu.ru/problem.php?contest=0&problem=176

题意:

给你N个点和一些这些点之间的边,每条边都有一个流量上界和流量下界,求1号结点

到N号结点的一个最小流,使得满足每条边的流量上下界的限制。

思路:

这是一道典型的有上下界的最小流的题目(求最大流是一样的算法),具体的方法介绍

请看这篇论文:《一种简易的方法求解流量有上下界的网络中网络流问题》。下面我就

大致地介绍一下这题的思路。首先很明显这是一个求有上下界流量的网络流的模型,我

们平时做得很多的都是只有一个流量上界的模型,这样我们就不能用原来的方法来处理

我们这里遇到的问题,在上面的论文中介绍了一种简易的求法,该算法的正确性是基于

下面的定理的:判断f是否是一个无源汇网络的可行流,可以通过判断该无源汇网络的改

进网络的源点的流出的流是否都是满流来实现,该改进网络的容量g[i] [j] = C[i][j] - B[i][j]

(具体请看论文)。这样我们就可以先二分答案,然后求出改进网络的最大流,判断是

否与源点流出的流相等,从而可以判断f是否是原无源汇的一个可行流。

代码:

#include<stdio.h>
#include<string.h>
#define CC(m,what) memset(m , what ,sizeof(m))
int N , M ;
const int inf = (1<<30) ;
const int MAXN = 110 ;
struct Node{
    int a , b ;
}edge[MAXN*MAXN] ;
int C[MAXN][MAXN] ,B[MAXN][MAXN];
int g[MAXN][MAXN] ;
int dis[MAXN] , gap[MAXN] , pre[MAXN] , cur[MAXN] ;

void checkmin(int &a, int b){
    a = a > b ? b : a ;
}
int sap(int s, int t, int nodenum){
    CC(dis, 0) ; CC(gap, 0) ; CC(cur , 0) ;
    int maxflow = 0 , u = pre[s] = s , aug = inf ;
    gap[0] = nodenum ;
    while( dis[s] < nodenum ){
loop :
        for(int v=cur[u] ; v<nodenum ; v++)
            if( g[u][v] && dis[u]==dis[v]+1){
                checkmin( aug, g[u][v] ) ;
                pre[v] = u ; u = cur[u] = v;
                if(v == t){
                    maxflow += aug ;
                    for(u=pre[u] ; v!=s ; v=u,u=pre[u]){
                        g[u][v] -= aug ;
                        g[v][u] += aug ;
                    }
                    aug = inf ;
                }
                goto loop ;
            }
        int mindis = nodenum ;
        for(int v = s ; v < nodenum ; v ++ ){
            if( g[u][v] && mindis > dis[v] ){
                cur[u] = v ;
                mindis = dis[v] ;
            }
        }
        if( (--gap[ dis[u] ]) == 0) break ;
        gap[ dis[u] = mindis + 1 ]++ ;
        u = pre[u] ;
    }
    return maxflow ;
}

bool is_ok(){
    int sum = 0 ;
    int s = 0 , t = N + 1;
    for(int i=1;i<=N;i++){
        int res = 0 ;
        for(int j=1;j<=N;j++){
            g[i][j] = C[i][j] - B[i][j] ;
            res += B[j][i] - B[i][j] ;
        }
        if( res > 0 ){
            g[s][i] = res ;
            sum += res ;
        }
        else{
            g[i][t] = -res ;
        }
    }
    int ans = sap(s , t , N + 2);
    if(ans == sum)  return 1 ;
    else            return 0 ;
}

int main(){
    int a, b, c, d ;
    while(scanf("%d%d",&N,&M)==2){
        CC(C,0) ; CC(B,0) ;
        for(int i=1;i<=M;i++){
            scanf("%d%d%d%d",&a,&b,&c,&d) ;
            edge[i].a = a ; edge[i].b = b ;
            C[a][b] = c ;
            if(d)   B[a][b] = c ;
            else    B[a][b] = 0 ;
        }
        int t1 = 0 ;
        for(int i=2;i<=N;i++){
            t1 += C[1][i] ;
        }
        t1++ ;
        int low = 0 , high = t1 , mid ;
        while(low < high){
            B[N][1] = 0 ;
            mid = (low + high) >> 1 ;
            C[N][1] = mid ;
            if( is_ok() ){
                high = mid ;
            }
            else{
                low = mid + 1 ;
            }
        }
        if(low == t1){
            printf("Impossible\n");
            continue ;
        }
        printf("%d\n",low);
        C[N][1] = low ;
        is_ok() ;
        for(int i=1;i<M;i++){
            a = edge[i].a ;
            b = edge[i].b ;
            printf("%d ",C[a][b]-g[a][b]) ;
        }
        a = edge[M].a ; b = edge[M].b ;
        printf("%d\n",C[a][b]-g[a][b]) ;
    }
    return 0 ;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值