书上所说的一般预流推进算法往往涉及很多概念比较繁琐,下面我按照自己的理解,通过一道简单的习题,来讲述一下一般预流推进算法,可能有不正确的地方,希望有大神能够给我指点。
现在假设读者已经了解了网络流的基本概念和一般增广路算法。
以单源汇网络流为例,最大流求的是单位时间内能够从源点S流到汇点T的最大流量(容量)。但是一般增广路算法实现相当麻烦。因为这种方法必须每次找一条增广路,然后沿着这条增广路进行增广,这就好像一开始所有的汽车全部被堵在了源点S,(假设这些汽车都迫切到达汇点T),然后一般增广路算法的实现就好像一名交警从源点S随便走了某一条路线到达了汇点T,发现这条路线上所有的线段的权值最小值是某个确定的值Limit,然后交警返回到原点S,汇报了他的结果:从S到T沿着他刚才走过的路径同时开可以“畅通无阻”不堵车,然后第二个交警从S开出走了另一条与之前那个交警不完全相同的路线到达T,回来报告了沿着他刚才走过的路径同时开Limit'辆车可以“畅通无阻”不堵车,如此循环,直到最后一名交警走完了最后一条能够到达T的路线回来汇报结果,交警长官最终得到一个结论:前面Limit辆车走第一条路线,接下来Limit'辆车走第二条路线,接下来。。。。。。
是不是觉得这样做有些繁琐呢?虽然这种方法实现起来比较容易理解,但是这种方法等于用深度优先搜索(DFS)吧所有情况都搜索了一遍,太耗时了!
有没有优化的方法?其实交警长官可以派若干个交警驻守在各个交叉路口,并且交警长官采取了无为而治的策略。那么,现在假设和交叉口S隔着一条街相邻的交叉路口只有S1和S2,连接S和S1的街最多能同时开过3辆车,连接S和S2的街最多能同时开过8辆车,那么我们现在就可以直接让3辆车从S开到S1,直接让8辆车从S开到S2,因为S1,S2这些交点都有交警把守,他们也会像S点处的交警一样进行处理,那么最后能够到达T的车的单位时间的辆数=∑min(Si,Pi),其中Si表示所有和T直接一条线段相连的交点,Pi是Si和T之间的那条线段的最大容量。
这就是一般预流推进算法的Mr.OneSong描述。
我们首先分析的是源点,其次分析的是源点能够到达的所有点。因为我们不可能让这“所有点”在同一时间被处理,所以我们将这些待处理的点加入到队列中,因为考虑到多个中间源点到达同一个中间汇点的情况,所以有些点需要多次进队列:
那么对于什么情况需要将一点加入队列或者重新加入队列呢?其实只有两种情况:
①当从某一点S'流到所有的和S'相连的Ti'时,如果S'还有残余流量,S'将进队列,因为这个时候S'中还存有一种“势能”,所以他还有往后流的可能
②当从某一点S'流到和S'相连的某一点Ti'时,因为S'把他的一部分残留流量给了T',所以T'从某种意义上来说变成了和S'一样的点,需要将其代入队列中进行分析
如果按照上述的方法,虽然现实当中司机师傅都能很好的保证汽车最大的通过量,但是在电脑模拟起来可能会造成一些问题,比如说一辆车从Si点开到Sj点后发现从Sj点也行不同,于是又开到了Si,于是一直在Si和Sj之间陷入了循环。有很多方法能够解决这个矛盾,所以这里用一个残留量函数RE[i][j]表示所有连接Si到Sj的所有线路的流量总和:比如说对走过的路径进行标记,标记结果就是对满流的线段更新其可残留量为0,不满流的其残留量相应减少;同时我们用一般预流推进算法实现的是类似广度优先搜索(BFS)的算法,不能像DFS一样很好的标记和撤销标记,所以这里我们用一个势能函数H[]来确定流是否可行。
初始时设源点的势能函数H[S]=V'>=V-1(V代表顶点数),这是为了确保S的势能能够确保S上的流能够到达图上的所有其他V-1个点;其他点的势能函数H[i]=0。
规定两点Si能够将其上的残留流量流到与其临接的Sj上当且仅当下面两种情况的其中一种成立:Si==S或H[i] - H[j] == 1。这样就保证了该算法陷入死循环。
例题:输入图的边数E和点数V,接下来每行3个数表示各边的起点,终点和该边的最大流量,求从1为源点到V为汇点的最大流。
样例输入:
4 4
1 2 2
1 3 3
2 4 1
3 4 6
样例输出:
4
#include <stdio.h>
#include <queue>
using namespace std;
#define oo (1<<29)
const int maxn = 110;
int n , m;
int RE[maxn][maxn] , EF[maxn] , H[maxn];
int S , T , Max_Flow;
queue<int> Q;
int min(int a , int b)
{
return a < b ? a : b;
}
void init()
{
Max_Flow = 0;
S = 1; T = n;
int i;
H[S] = n;
EF[S] = oo;
EF[T] = -oo;
}
void Push(int Si)
{
int i , Tmp;
for(i=1;i<=n;i++)
{
Tmp = min(RE[Si][i] , EF[Si]);
if(Tmp > 0 && (Si == S || H[Si] - H[i] == 1))
{
RE[Si][i] -= Tmp; RE[i][Si] += Tmp;
EF[Si] -= Tmp; EF[i] += Tmp;
if(i == T) Max_Flow +=Tmp;
if(i != T && i != S) Q.push(i);
}
}
return;
}
void Relabel(int Si)
{
if(Si != S && Si != T && EF[Si] > 0)
{
H[Si] ++;
Q.push(Si);
}
return;
}
void Push_Relabel()
{
int Si;
init();
Q.push(S);
while(!Q.empty())
{
Si = Q.front();
Q.pop();
Push(Si);
Relabel(Si);
}
printf("%d\n",Max_Flow);
}
int main()
{
freopen("pushrelabelin.txt","r",stdin);
freopen("pushrelabelout.txt","w",stdout);
scanf("%d%d",&m,&n);
init();
int i;
int u , v , w;
for(i=1;i<=m;i++)
{
scanf("%d%d%d",&u,&v,&w);
RE[u][v] += w;
}
Push_Relabel();
return 0;
}
posted on 2012-08-24 10:42
Mr.OneSong 阅读(3435)
评论(0) 编辑 收藏 引用