网络流初步:<最大流>——核心(增广路算法)

终于开始接触网络流了;

网络流到底是个虾米东东,用比较学术的话说,就是

  一个有向图 G=(V,E);

  有两个特别的点:源点s、汇点t;

  图中每条边(u,v)∈E,有一个非负值的容量C(u,v)

记为 G=(V,E,C)

网络三要素:点、边、容量

用我的其中,最不好理解的就是容量和流量,对于初学者,应该这样理解,每条边有c表示最多能运送多少货物,而流量f表示最多当前运送的货物多少。

这样要好点嘿嘿。

解决了网络流之后,我们就要想,最大流。

what is最大流

在我们拥有网络流的概念后,我们想啊,最大流,就是起点到终点的最大流量。

其中,在最大流的问题中,我们要满足三个条件,1:容量限制:f(u,v)<c(u,v);

2:斜对称性:(f(u,v)=-f(v,u))在后面的运用中,我们叫做一旦有物品从u运到v,那么则肯定有一个可退流从v运到u

3:流量平衡:简单来说就是除了源点和汇点,没有其他点可以保存货物。显然f(s,u)==f(v,t);

既然这样,我们的目的就是使f(s,u)和f(v,t)最大

怎么使目标最大化呢,现在,就要介绍增广路算法,这非常重要:可以说怎个网络流都必须有增广路算法的影子。

好,我们先想,如果一个网络流还有一条增广路,按照我们的理解,那么这条路上所有的弧都可以让x个货物通过,那么一定不是最大流

比如现在;

还存在S--A---C---T的可增广路,那么就一定不是最大流

但是反过来,如果一个网络没有增广路,那么他一定是最大流吗,有的人说是的,当然啦。但是请看

这也是一个没有增广路的网络,但是,他是最大流吗。

我们发现,还可以这样。orz

所以,刚才那个最多算一个极大流,而不是最大流(什么虾米东东)

也就是说从B出发,本来有两条路可以走的,但是B却走了错误的路,所以它成功地把C---T给堵住了。

我们又称之为“阻塞流”。也就是说一个错误的路让其他可行流被阻塞了。

怎么改进呢,反正B出发只有两条路,都要试一试对吧,既然上面那条是阻塞流。

那下面这条呢,试一试不就知道了,那我们就把B--c压回去,走另外一条路。

 现在。就是这样

但是,这肿么实现呢,如果模拟将流压回去,忒难了点吧。

 所以,我们引入了反向弧,借助他来推流。

增广路径(可改进路径)的定义

    若P是网络中连结源点s和汇点t的一条路,我们定义路的方向是从s到t,则路上的弧有两种:

l  前向弧---弧的方向与路的方向一致。前向弧的全体记为P+;

l  后向弧---弧的方向与路的方向相反。后向弧的全体记为P-;

    设F是一个可行流,P 是从s到t的一条路,若P满足下列条件:

在P+的所有前向弧(u,v)上,0≦f(u,v) < C(u,v);

在P-的所有后向弧(u,v)上,0<f(u,v) ≦C(u,v);

    则称P是关于可行流F的一条可增广路径。

有点难理解对吧。

但是我们成功了。

现在,网络变成了这个样子。

其实,为什么反向弧退流就对了呢,我认为是这样的,对于一条已经有了可退流的弧,一旦退流,其退流一定可以将退流退完,所以,满足三要素。就一定是对的。

所以,增广路定理就是:当一个残量网路上没有增广路了,那么他一定是最大流。

基于这个定理,我们可以设计出算法。

在一个残量网络上找增广路增广,然后,一旦没有增广路,就一定是最大流。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 10000+10
#define M 10000+10
#define inf 1e9;
using namespace std; 
int head[N],arnum=1,used[N],ans,way[M];
struct ss{int next,to,cap;}a[M];
void add(int from,int to,int cap){a[++arnum]=(ss){head[from],to,cap};head[from]=arnum;}
void insert(int u,int v,int cap){add(u,v,cap),add(v,u,0);}
int n,m,S,T;
void work(int step)
{
    int minn=inf;
    for(int i=1;i<=step;i++)
        minn=min(minn,a[way[i]].cap);
    ans+=minn;
    for(int i=1;i<=step;i++)
    {
        a[way[i]].cap-=minn;
        a[way[i]^1].cap+=minn;
    }
}
int dfs(int u,int step)
{
    for(int i=head[u];i;i=a[i].next)
    {
        int v=a[i].to;
        int cap=a[i].cap;
        if(not used[v] and cap>0)
        {
            way[step]=i;
            used[v]=1;
            if(v==T){work(step);return 1;}
            else if(dfs(v,step+1))return 1;
        }
        return 0;
    }
}
int main()
{
    scanf("%d%d%d%d",&n,&m,&S,&T);
    int u,v,c;
    for(int i=1;i<=m;i++){scanf("%d%d%d",&u,&v,&c);insert(u,v,c);}
    for(;;)
    {
        memset(used,0,sizeof(used));
        used[S]=1;
        if(not dfs(S,1))break;
    }
    printf("%d",ans);
    return 0;
}

 

这就是最低级的算法吧O(∩_∩)O哈哈~。

附上代码

 

posted @ 2017-10-01 12:00  star_eternal  阅读(5341)  评论(0编辑  收藏  举报