算法_dinic最大流

网络流是什么?

不急我们慢慢来讲。

首先我们先看看最大流

1.背景

管道网络中每条边的最大通过能力(容量)是有限的,实际流量不超过容量。最大流问题(maximum flow problem),一种组合最优化问题,就是要讨论如何充分利用装置的能力,使得运输的流量最大,以取得最好的效果。求最大流的标号算法最早由福特和福克逊与与1956年提出,20世纪50年代福特(Ford)、(Fulkerson)建立的“网络流理论”,是网络应用的重要组成成分。

2.定义(这很重要!!!)

1)源点:只有流出去的点

2)汇点:只有流进来的点

3)流量:一条边上流过的流量

4)容量:一条边上可供流过的最大流量

5)残量:一条边上的容量-流量

2.性质

1)对于任何一条流,总有流量<=容量(流经的边的最小流量),即
\[flow<=min \left\{cur_{i}\right\}(cur_i\in flow_{now})\]
2)对于任何一条有向边(u,v),总有
\[cur(u,v)=-cur(v,u)\]

3.算法思想(增广路dinic)

1.找到一条从源点到汇点的路径,使得路径上任意一条边的残量>0(注意是小于而不是小于等于,这意味着这条边还可以分配流量),这条路径便称为增广路

2.找到这条路径上最小的\(F[u][v]\)(我们设\(F[u][v]\)表示\(u->v\)这条边上的残量即剩余流量),下面记为\(flow\)

3.将这条路径上的每一条有向边\(u->v\)的残量减去\(flow\),同时对于起反向边\(v->u\)的残量加上\(flow\)(为什么呢?我们下面再讲)

4.重复上述过程,直到找不出增广路,此时我们就找到了最大流
增广路过程

这个图很详细的给出了增广路的过程,其实这就是dinic算法的思想

4.时间复杂度

dinic算法其实就是一种暴力,时间复杂度
\[O(n^2m)\]
这其实十分暴力了,如果\(m=\frac{n(n-1)}{2}\)d的话,那么时间复杂度将达到
\[O(\frac{n^3(n-1)}{2})\]
省略常数后也就是
\[O(n^4)\]
这是极其恐怖的复杂度,当然如果出题人不故意卡的话,一般没什么问题

5.代码实现

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define Maxn 1000001
#define INF 0x7fffffff
#define maxN 1000001
#define maxM 1000001
using namespace std;

class Dinic{
    private:
        int cnt;//边的数量,从0开始编号。
        int head[Maxn];//每一个点最后一条边的编号
        int next[Maxn];//指向对应点的前一条边
        int v[Maxn];//每一条边指向的点
        int w[Maxn];//每一条边的残量
        int n,s,t;//源点和汇点
        int depth[Maxn];//分层图中标记深度
        void add_edge(int x,int y,int z){
            next[++cnt]=head[x];
            v[cnt]=y;
            w[cnt]=z;
            head[x]=cnt;
            return;
        }
    public:
        void init(int x,int y,int z){//初始化
            n=x,s=y,t=z;
            cnt=-1;
            memset(head,-1,sizeof(head));
            memset(next,-1,sizeof(next));
            return;
        }
        void addedge(int x,int y,int z){
            add_edge(x,y,z);
            add_edge(y,x,0);
            return;
        }
        bool bfs(){//广搜找分层图 
            queue<int>q;
            memset(depth,0,sizeof(depth));
            depth[s]=1;
            q.push(s);
            do{
                int u=q.front();
                q.pop();
                for(int i=head[u];~i;i=next[i]){
                    if(w[i]>0 and !depth[v[i]]){
                        depth[v[i]]=depth[u]+1;
                        q.push(v[i]);
                    }
                }
            }while(!q.empty());
            return depth[t];
        }
        int dfs(int u,int now){
            if(u==t)
                return now;
            for(int i=head[u];~i;i=next[i]){
                if(w[i]>0 and depth[v[i]]==depth[u]+1){
                    int d=dfs(v[i],min(now,w[i]));
                    if(d>0){
                        w[i]-=d;w[i^1]+=d;
                        return d;
                    }
                }
            }
            return 0;
        }
        int dinic(){//dinic算法主过程
            int ans=0;
            while(bfs()){
                int x;
                while(x=dfs(s,INF))
                    ans+=x;
            }
            return ans;
        }
}dinic;
int n,m,s,t;


int main(){
    scanf("%d%d%d%d",&n,&m,&s,&t);
    dinic.init(n,s,t);
    for(int i=1;i<=m;i++){
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        dinic.addedge(x,y,z);
    }
    printf("%d\n",dinic.dinic());
    return 0;
}

6.最大流能解决的问题

网络流的问题其实有着很大的共性,首先就是他们都是只需要求一个的答案,且不需要中间的过程,还有答案是某种最值,往往这就是网络流。而网络流也不是裸的,往往需要各种建模乱 搞 。

猜你喜欢

转载自www.cnblogs.com/ezoihy/p/9123541.html