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


    终于开始接触网络流了;

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

      一个有向图 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哈哈~。

    附上代码

  • 相关阅读:
    初识python
    如何通过发新浪微博关闭电脑
    如何给word 文章的每段段尾添加 脚注
    三种可视化格式模型:普通文档流、相对定位与绝对定位、浮动
    Python基础知识:函数
    比较两个数的大小,自定义比较两个整数的大小的方法
    编程输出九九乘法表
    [2012-06-21]结合find的awk
    [2012-05-31]awk去重复项
    [2012-05-31]awk记录分割符RS
  • 原文地址:https://www.cnblogs.com/star-eternal/p/7616967.html
Copyright © 2020-2023  润新知