zoukankan      html  css  js  c++  java
  • 网络流之最大流

    暑假就已经学过了,可是因为我太菜了,所以完全没懂,今天老师要我们落实之前学的,就复习了网络流

    虽然CSP不考,但是学了也可以用(就像去年D2T3,动态DP模板)

    好了,接下来进入正题:

    首先,啥子事网络流,官方解释如下

    在图论中,网络流(英语:Network flow)是指在一个每条边都有容量(capacity)的有向图分配流,使一条边的流量不会超过它的容量。
    通常在运筹学中,有向图称为网络。顶点称为节点(node)而边称为弧(arc)。一道流必须匹配一个结点的进出的流量相同的限制,除非这是一个源点(source)──有较多向外的流,
    或是一个汇点(sink)──有较多向内的流。一个网络可以用来模拟道路系统的交通量、管中的液体、电路中的电流或类似一些东西在一个结点的网络中游动的任何事物。

    嘿嘿,好像不懂,简单一点来说:

    有一个储水站,给一个小区分配水,而你又住在里面

    每家每户都有不同规格的水管,也就是说,在速度不变的情况下,同一时间内,流动的水的多少不同

    而且从储水站里面送出来的速度必须一样,因为流速越快,液体压强越大

    我们当然不希望水管炸裂

    每一户人家用完的水肯定要运输到一个废水处理站

    我们需要求的是储水站最多可以送出多少水,最后能够送到废水处理站(因为要回收利用)

    我第一次看到这个题目时,产生了一段对话:

    我:这个不就是暴力枚举吗???
    某巨佬:......这个超时了
    我:......那怎么做
    某巨佬:EK或者是Dinic
    我:???什么玩意儿

    首先明确几个概念:

    容量:每条边都有一个容量(水管的最大水流容量)

    源点:出发点(储水站)。

    汇点:结束点(废水处理站)。

    流:一个合法解称作一个流,也就是一条可以从源点到汇点的一条合法路径。

    流量:每条边各自被经过的次数称作其流量,最终收集的总数为整个流的流量。

    增广路:在当前网络之后找到一条能够从源点到汇点能运更多货物的路径。当一条边被增广之后(即它是增广路的一部分,或者说增广路通过这条边),这条边还能通过的流量,叫做剩余流量,修改之后的图称为残量网络。

    首先是我最喜欢的EK算法(主要是代码易懂,简洁,适合我这种蒟蒻)

    它的思想很简单,每一次疯狂的寻找增广路,当然如果其中有一条边为‘0’,就不是增广路,将当前的流量减去中间的最小值(肯定的,因为如果不是最小值水就不能通过)

    ans加上当前这个最小值,直到寻找不到增广路

    但是有瑕疵,可以把这个hack掉:

    我们可以轻易知道,答案为2,但是如果按照以上思想:

    程序会这样跑,以至于ans=1:

    所以,我们需要给这个程序一个后悔的机会

    是不是想到dfs回溯,很明显,超时了

    所以我们想到了一个巧妙的办法,建反边,初始值为零

    在找增广路的时候,设这条路的最小流量为opt

    那么,反边的值+opt,正边-opt

    我们可以这么想,我们走反的边相当于将水退了回去

    好的,就这样,AC代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    
    const int N=300002;
    int spot,EDGE,S,E;
    int head[N],ver[N],nxt[N],tot,edge[N];
    int q[N],hd,tl,res[N],now[N],pre[N],ans;
    
    int read(){
        int s=0,w=1;char ch=getchar();
        while(ch<'0'||ch>'9')w=(ch=='-')?-1:1,ch=getchar();
        while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
        return s*w;
    }
    
    void write(int x){
        if(x<0) {putchar('-');x=-x;}
        int y=10,len=1;
        while(y<=x) {y*=10;len++;}
        while(len--){y/=10;putchar(x/y+48);x%=y;}
    }
    
    void add(int x,int y,int z){
        ver[++tot]=y;edge[tot]=z;nxt[tot]=head[x];head[x]=tot;
    }
    
    bool Bfs(){
        memset(res,0,sizeof(res));
        hd=0;tl=1;q[1]=S;res[S]=1;now[S]=1e9;
        while(hd^tl){
            hd++;
            int x=q[hd];
            for(int i=head[x];i;i=nxt[i]){
                int y=ver[i];
                if(!res[y]&&edge[i]>0){
                    now[y]=min(now[x],edge[i]);pre[y]=i;
                    if(y==E)return 1;
                    tl++;q[tl]=y;res[y]=1;
                }
            }
        }
        return 0;
    }
    
    void EK(){
        ans+=now[E];
        int x=E;
        while(x!=S){
            edge[pre[x]]-=now[E];edge[pre[x]+1]+=now[E];
            x=ver[pre[x]+1];
        }
    }
    
    int main(){
        EDGE=read();S=1;E=read();
        for(int i=1;i<=EDGE;i++){
            int x=read(),y=read(),z=read();
            add(x,y,z);add(y,x,0);
        }
        while(Bfs())EK();
        write(ans);
        return 0;
    }

    //   10-2   //

  • 相关阅读:
    3.无重复字符的最长字串
    k-近邻算法(KNN)
    决策树
    解决anaconda与pycharm冲突导致import无法使用
    2.两数相加
    1.两数之和
    数学建模第七章 数理统计
    数学建模第六章 微分方程建模
    Web(4)servlet
    阿里云卸载自带的JDK,安装JDK完成相关配置
  • 原文地址:https://www.cnblogs.com/GMSD/p/11617827.html
Copyright © 2011-2022 走看看