例题是hdu 1532,一道网络流模板题,可以给大家练练手。
http://acm.hdu.edu.cn/showproblem.php?pid=1532
#include<stdio.h> #include<string.h> #define MAX 202 #define INF 999999999 bool 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] )隐含的给出了图已经被转化为双向图了(虽然从输入里看不出).