最近在看网络流,看了算法导论不是很懂。。不知道书里那个图是不是错了,搞得我有点混乱;
然后就从网上查下资料,翻了几个大神的博客,就搞懂了个大概是怎么回事;网络流里有很多
算法,因为是入门,所以就写了个EK(Edmonds-Karp)求最大流的算法,顺便做了个模板题;
写的时候还行,大概思路没什么问题,就是实现的时候有点小错误,下面是本人学习中的一些理解:
Edmonds-Karp求最大流的算法核心思想就是不断找增广路,每找到一条增广路,记录增广路中
边流量最少的值d,然后让当前找到增广路中的每一条正向弧减去d,让反向弧加上d,当不再找
到增广路的时候,就证明当前的总流量已经是最大流;至于怎样找增广路。。一致认为BFS是最好
的方法了。。。
增广路(摘自NOCOW):设f 是一个可行流,W是发点到收点的一条有向道,如果W满足下列条件,称之为关于可行流f的一条可增广道
(又称可扩道):
1. 每条正向弧是非饱和弧,
2. 每条反向弧是非零流弧.
做完题之后,发现如果不加反向弧好像也可以算出答案,但是交上去又WA,然后百度搜了下
为什么要有反向边或者残余图?
前面我们确定了一条路径的最小流量之后,我们实际上可以把这部分流量从图中删去(这个可以看做减治的过程):因为这个路径以后是不会更改的,而且路径之间互不影响。但是也会出现一定的问题:结果往往不是最优的。反向边和残余图可以解决这一问题。
最近又看了算法导论之后,看到了个可以作为不加反向弧而得不到最优解的反例,如下图
第一张图是最原始的图,值为边的容量,第二张图是找出s->v1->v2->t增广路后不加反向弧的残余图;明显的,如果不加反向弧,v1->v2一旦成为饱和弧后,就不能恢复,而这样最后算出来的流值是199,但是正确的最大流值是200,所以不加反向弧不能保证得到的是最优解(这算是证明么- -!);
POJ 1273(Drainage Ditches),可以用来测试下代码写得对不对,第一次做网络流的题,纪念下。。
#include
#include
#include
#include
using namespace std;
const int inf=0xfffff;
const int N = 330;
int maxflow, pre[N], dp[N][N];
void Edmonds_Karp(int start, int end, int m){
while(1){
queue p;
int minflow = inf;
p.push(1); //源点为1,进队
memset(pre, 0, sizeof(pre));//初始化增广路径数组,题目中的顶点是从1开始的
while(!p.empty()){ //bfs找增广路
int u = p.front();
p.pop();
if(u == end)
break;
for(int i = 1;i <= m;i++)
if(dp[u][i] > 0&&pre[i] == 0){ //pre[i]除了记录当前顶点的父亲,还记录当前顶点有没被访问过
pre[i] = u;
p.push(i);
}
}
if(pre[end] == 0) //顶点的父亲为空,表示找不到增广路,很容易理解吧。。
break;
for(int i = end;i != start;i = pre[i]) //找出增广路中最小残余量
minflow = min(minflow, dp[pre[i]][i]);
for(int i = end;i != start;i = pre[i]) {
dp[pre[i]][i] -= minflow; //更新增广路中正反向弧的流量
dp[i][pre[i]] += minflow;
}
maxflow+=minflow;
}
}
int main(){
int n, m;
while(~scanf("%d%d", &n, &m)){
int i, a, b, f;
memset(dp, 0, sizeof(dp));
for(i = 0;i < n;i++){
scanf("%d%d%d", &a, &b, &f);
dp[a][b] += f;
}
maxflow = 0;
Edmonds_Karp(1, m, m);
printf("%d\n", maxflow);
}
return 0;
}