Mr.一首歌
能不能给我一首歌的时间,我只有三分钟的热度
posts - 4,comments - 0,trackbacks - 0
  书上所说的一般预流推进算法往往涉及很多概念比较繁琐,下面我按照自己的理解,通过一道简单的习题,来讲述一下一般预流推进算法,可能有不正确的地方,希望有大神能够给我指点。
  现在假设读者已经了解了网络流的基本概念和一般增广路算法。
  以单源汇网络流为例,最大流求的是单位时间内能够从源点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 阅读(3428) 评论(0)  编辑 收藏 引用

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理