• 网络流最大流入门(Dinic算法)模板


    前言

    本来先搞计算几何再搞网络流的,但是**总是发网络流的题,然后天天被信息组巨佬爆踩,所以先学一下最基本的Dinic算法吧。我也只是大致理解了流程,其实不懂也没事,只要会堆代码就好了(QAQ),所以下面只有教你如何堆代码啦(……)。

    基本概念

    图和收发点

    一个图是由点集V={vi}和V中元素的无序对的一个集合E={ek}所构成的二元组,记为G=(V,E),V中的元素vi叫做顶点,E中的元素ek,叫做边。
    仅有一个入次为0的点vs称为发点(源),一个出次为0的点vt称为收点(汇),其余点为中间点,这样的网络G称为容量网络,常记做G=(V,E,C)。

    容量和流量

    设有向连通图G=(V,E),G的每条边(vi,vj)上的非负数cij称为边的容量。对任一G中的边(vi,vj)有流量fij,称集合f={fij}为网络G上的一个流。右图即为一个有向连通图,括号中第一个数字代表容量,第二个数字代表流量。

    可行流

    称满足下列两个条件的流为可行流:
    1.容量限制条件:对G中的每条边(vi,vj),有0≤fij≤cij;即每条边上的流量非负而且最大也只能达到容量的限制。
    2.平衡条件:对中间点vi,有
     ,即物资的输入量和输出量相等。
    对发、收点vs,vt,,有
     ,fij为网络流的总流量。
    一个流f={fij},当fij=cij,则称流f对边(vi,vj)是饱和的,否则称f对(vi,vj)不饱和。
     

    用途

    当然就是求有向图中指定的某点到另一点的最大可行流(自己归纳的),解决这一问题的最好的算法就是Dinic了,下面给出具体流程。

    算法流程

    用n表示点数,m表示边数,st表示要求的起点,ed表示要求的终点(不专业术语,不要模仿呐)

    首先存边

    int head[],tot;
    struct edge{int v,w,nxt;}e[];
    void addn(int u,int v,int w){ 
        e[++tot]=(edge){v,w,head[u]};
        head[u]=tot;
    }
    //存边:u->v(w)和v->u(0)
    addn(u,v,w);
    addn(v,u,0);

    然后码出每次dfs前需要的bfs建图

    用cur[ ]代替变化的head[ ],dis[ ]表示到每个点到st的距离(每隔一条边距离就是1)

    int bfs(int st,int ed)
    {
        //bfs建图 
        queue<int>que;
        memset(dis,-1,sizeof(dis));
        dis[st]=0;
        que.push(st);
        while(!que.empty())
        {
            int x=que.front();
            que.pop();
            for(int i=head[x];i;i=e[i].nxt)
            {
                int now=e[i].v;
                if(dis[now]==-1&&e[i].w)
                {
                    que.push(now);
                    dis[now]=dis[x]+1;
                }
            }
        }
        return dis[ed]!=-1;
    }

    接着码dfs查询

    int dfs(int x,int t,int maxflow)
    {
        if(x==t) return maxflow;
        int ans=0;
        for(int i=cur[x];i;i=e[i].nxt)
        {
            int now=e[i].v;
            if(dis[now]!=dis[x]+1||!e[i].w||ans>=maxflow) continue;
            cur[x]=i;
            int f=dfs(now,t,min(e[i].w,maxflow-ans));
            e[i].w-=f;
            if(i&1) e[i+1].w+=f;
            else e[i-1].w+=f;
            ans+=f;
        }
        if(!ans) dis[x]=-1;
        return ans;
    }

    然后大致流程就出来了,不断重复:bfs建图,dfs查询改值,直到不能再进行

    现在放出全部代码

    //dinic板子 
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #define INF 0x3f3f3f3f
    using namespace std;
    const int N=10000,M=100000;
    int n,m,tot,ss,dd,head[N+3];
    
    struct edge{int v,w,nxt;}e[M*2+3];
    
    int dis[N+3],cur[N+3];
    
    void addn(int u,int v,int w){e[++tot]=(edge){v,w,head[u]};head[u]=tot;}
    
    int bfs(int st,int ed)
    {
        //bfs建图 
        queue<int>que;
        memset(dis,-1,sizeof(dis));
        dis[st]=0;
        que.push(st);
        while(!que.empty())
        {
            int x=que.front();
            que.pop();
            for(int i=head[x];i;i=e[i].nxt)
            {
                int now=e[i].v;
                if(dis[now]==-1&&e[i].w)
                {
                    que.push(now);
                    dis[now]=dis[x]+1;
                }
            }
        }
        return dis[ed]!=-1;
    }
    
    int dfs(int x,int t,int maxflow)
    {
        if(x==t) return maxflow;
        int ans=0;
        for(int i=cur[x];i;i=e[i].nxt)
        {
            int now=e[i].v;
            if(dis[now]!=dis[x]+1||!e[i].w||ans>=maxflow) continue;
            cur[x]=i;
            int f=dfs(now,t,min(e[i].w,maxflow-ans));
            e[i].w-=f;
            if(i&1) e[i+1].w+=f;
            else e[i-1].w+=f;
            ans+=f;
        }
        if(!ans) dis[x]=-1;
        return ans;
    }
    
    int Dinic(int st,int ed)
    {
        int ans=0;
        while(bfs(st,ed))
        {
            memcpy(cur,head,sizeof(head));
            int k;
    //        ans+=dfs(st,ed,INF); 
            while(k=dfs(st,ed,INF)) ans+=k;
        }
        return ans;
    }
    
    
    int main()
    {
        scanf("%d %d %d %d",&n,&m,&ss,&dd);
        for(int i=1;i<=m;i++)
        {
            int u,v,w;
            scanf("%d %d %d",&u,&v,&w);
            addn(u,v,w);
            addn(v,u,0);
        }
        cout<<Dinic(ss,dd);
        return 0;
    }

    注意:题目不同,m和n的大小也不同,记得要更改呀~

    完结~~第一篇学术文章;撒花撒花✿✿ヽ(°▽°)ノ✿

  • 相关阅读:
    Java并发编程:线程池的使用
    AlarmManager与PendingIntent
    ConnectivityManager与检查网络连接的使用
    IntentService的使用
    Service(Local Service)简介
    Looper、Hander、HandlerThread
    XML_PULL解析
    android AsyncTask 的使用(转载)
    android 连接网络的简单实例
    xml drawable
  • 原文地址:https://www.cnblogs.com/mm-puppy-lyt/p/mm-puppy-wllzdlDinic.html
Copyright © 2020-2023  润新知