网络流详解(2)

来源:互联网 发布:淘宝获取商品id 编辑:程序博客网 时间:2024/05/19 17:58
例题是hdu 1532,一道网络流模板题,可以给大家练练手。
http://acm.hdu.edu.cn/showproblem.php?pid=1532
#include<stdio.h>#include<string.h>#define MAX 202#define INF 999999999bool bfs( int M );void up_date( int k );int  Min( int a , int b ){ return a < b ? a : b ; }int min;int rest[MAX][MAX] , pre[MAX];//rest[i][j]表示i点到j点是否可灌水(非零就表示可灌)//pre[k]表示BFS寻找增广路径时k的前驱int main( ){    int N , M , sum;    int i , j , a , b , c ;    while( ~scanf("%d%d",&N,&M) ){memset( rest , 0 , sizeof( rest ) );    //开始前rest为0,即没有管道,不可灌水for( sum=0 , i=1 ; i<=N ; i++ ){scanf("%d%d%d",&a,&b,&c);rest[a][b] += c ;    //处理重边情况}while( bfs( M ) ){min = INF;    //min为全局变量up_date( M );sum += min;}printf("%d/n",sum);    }    return 0;}bool  bfs( int M ){    bool mark[MAX];    int  queue[MAX] , top , rear;    int  s , k ;    top = rear = 0;    memset( mark , false , sizeof( mark ) );    queue[rear++] = 1;    //从源点开始搜索    mark[1] = true;    while( top!=rear ){s = queue[top++];if( s==M ){    //BFS搜到了汇点return true;}for( k=1 ; k<=M ; k++ ){if( !mark[k] && rest[s][k] ){    //rest[i][j]不为零的时候可表示正向或反向边    mark[k] = true;    queue[rear++] = k;    //加入队列    pre[k] = s;    //记录前驱}}    }    return false;}void up_date( int k ){    //更新每根管道的值(看做双向管道)    if( k==1 )    //递归达到深度return ;    int s=pre[k];    min = Min( min , rest[s][k] );    //正向边负向边统一处理    up_date( s );    //递归到最深状态    rest[s][k] += min;    //正向管道加去min值    rest[k][s] -= min;    //负向管道减上min值(反向管道少了的水量被正向管道使用,即实现了"隐含"双向管道)}

//原版主在这里正向管道是减去min值,负向管道是加上了min值,但是我转载的时候改了,如果有错,希望各位神犇指出,谢谢了!(我也是正在看网络流最大流,所以想分享给同我一样的蒻苣。。。
算法分析:题目意思很明确,是求网络流中的最大流问题,该问题我采用了Fold-Fulkerson(福特-福克森)算法,算法分为两个部分,求增广路径和更新路径值,增广路经就是有向图中的源点到汇点的路径中所有正向边流量<最大流量;负向边中流量>0则称该路径为增广路径;算法求出增广路径上的权值最小值(管道剩余排水量),该路径可用增加这么多的排水量.依次执行算法到找不到增广路经为止.注意:增广路虽然是源点到汇点的一条路径,不过不能首先dfs一次性找出所有路径,因为在每次更新的时候可能其他某些路径已经不是增广路径了.该算法中rest[正]+=min,rest[负]-=min,bfs中的判断if( !mark[k] && rest[s][k] )隐含的给出了图已经被转化为双向图了(虽然从输入里看不出).

原创粉丝点击