zoukankan      html  css  js  c++  java
  • 省赛在即!最大流问题回顾学习!!DInic

    Dinic是很好的算法,但是我还是从ek算法复习起步

    面对最大流问题,印象最深的就是反向边的思想,他给我们提供了反悔的机会,其实现在放到实际上来想,可以相当于两边的水都流了这条边,只是方向不一样,放到程序上,就是添加反向边。

    ek算法是基础的算法,思想也比较简单,就是先用bfs去寻找一波可行的1  到  n 的最大流,然后记录每一个经过的结点的前驱,在调用ek算法建立反向边,时间上面也是很费时,所以才有必要去学习Dinic算法及其优化的版本,这里就不粘贴我的ek算法的代码了……

    果然是温故而知新,复习了一晚上Dinic算法,有对他有了新的理解和认识,相对于ek算法,dinic算法的优化真的是非常的好——当前弧优化

    #include <iostream>
    #include <string.h>
    #include <cstdio>
    #include <queue>
    #define inf 0xfffffff
    using namespace std;
    const int maxn = 220;//最大的结点数量
    const int maxm = 4e4 + 4e2;//边的数量
    int n,m;
    struct node{
        int pre;
        int to,cost;
    }e[maxm];
    int id[maxn],cnt;
    int flor[maxn];
    void init()
    {
        memset(id,-1,sizeof(id));
        cnt = 0;
    }
    int cur[maxn];
    void add(int from,int to,int cost)
    {
        e[cnt].to = to;
        e[cnt].cost = cost;
        e[cnt].pre = id[from];
        id[from] = cnt++;
        swap(from,to);
        e[cnt].to = to;
        e[cnt].cost = 0;
        e[cnt].pre = id[from];
        id[from] = cnt++;
    }
    

     上面都是基本的存储结构,链式前向星存储,反向边的建立,层数的记录

    先面整体观看一下Dinic算法

    int Dinic(int s,int t)
    {
        int ret = 0;
        while(bfs(s,t))//进行分层预处理
        {
            for(int i = 1;i <= n;i++)
            {
                cur[i] = id[i];
            }
            ret += dfs(s,t,inf);//dfs寻找最大增广路
        }
        return ret;
    }
    

    bfs进行分层处理,dfs进行最大增广路的寻找,cur数组时dfs中优化的一个关键,后面会提及

    先来看看bfs分层处理

    int bfs(int s,int t)
    {
        queue<int>q;
        while(q.size())q.pop();
        memset(flor,0,sizeof(flor));
        flor[s] = 1;
        q.push(s);
        while(q.size())
        {
            int now = q.front();
            q.pop();
            for(int i = id[now];~i;i = e[i].pre)
            {
                int to = e[i].to;
                int cost = e[i].cost;
                if(flor[to] == 0/*冲当了vis*/ && cost > 0/*还有流量*/ )
                {
                    flor[to] = flor[now] + 1;
                    q.push(to);
                    if(to == t)return 1;//分层到终点结束立即返回
                }
            }
        }
        return 0;
    }
    

     根据边的关系flor数组还充当vis数组,进行层级标记,为后续的dfs做准备,bfs何时返回呢要么时到了中点返回1,要么是到不了终点返回0

    精彩的时dfs,当前弧的优化思想太厉害太厉害了

    int dfs(int s,int t,int value)//起点,终点,当前流量
    {
        //寻找增广路
        int ret = value;
        if(s == t || value == 0)return value;//要么是路通了,要么是没路了
        int a;
        //找不到t了,因为到t的边流量都变成了0!!
    
        /*
      优化的时候记录优化到哪条边了 所以对于每一条边我只会访问一次 */ for(int &i = cur[s];~i;i = e[i].pre) { int to = e[i].to; if(flor[to] == flor[s] + 1 && (a = dfs(to,t,min(ret,e[i].cost)))) { e[i].cost -= a;//对于这条边的优化操作 e[i^1].cost += a; ret -= a;//最后返回的是ret -= a 所以我们是记录了a的 if(!ret)break;//ret == 0时 /* ret记录了s到to的最大流量值a呢是to到t的最大流量 当ret - a == 0 的时候就可以结束了,往前返回的是value值,进行后续边的优化 */ /* 为什么不相等的时候不会退出呢?? 不相等,也就是ret > a,这是后前面的路都至少还有ret - a的残量,可以继续dfs进行优化更新 */ } } if(ret == value)flor[s] = 0;//中间结点遍历了所有的边,得到的结果是无路(流量),所以标志中间结点s废掉 return value - ret; }

     先来放一张我画的惨图

    没错,一开始我对初始传参dfs(s,t,inf)传入inf不是太理解,第一次调用什么都没有连同,所以可流量时无限大的,也为了结下的递归做了铺垫

    然后dfs的递归设置结束的条件1.找到了2.当前可流量变成了零也就没必要继续找了不是??

    当前弧优化的经典就是cur数组,也就是id数组的副本,记录了边的信息,C++的引用确保了对于这个点的边信息我只会dfs一次,不会重复dfs这样就大大的节约了时间

    然后a接受的时to 到 t 的最大流量,返回之后层层进行边和反向边的更新ret -= a时什么意思呢??首先来看看ret表示的什么吧递归进来的时候ret和value都表示的时s 到 to的最大可流量,而a表示的时to 到 t 的最大流量,我们要对to前面的边进行优化,所以ret -= a表示的时s 到 to的那些点还有没有可流能力如果ret = 0那就不可再流直接返回,反之可以再留,就继续根据cur数组进行后面边的查询,直到查询结束,跳出的时候你可以判断一下如果当前s点后没有一条就可以标记s点为费点接下来的dfs回溯优化不会在考虑s点了,也是一个小小的优化吧

    到此,Dinic算法就告于段落了~~

    ——————————————————————————————————————————————————

    加油!!

  • 相关阅读:
    泛型<T>,是你肿莫了,还是我错了...
    点名系统 附源码
    飞行棋(士) [窗口 控制台 自定义 版] 附源码
    C# 自定义常用代码段快捷键
    C++ MFC 操作文件夹及属性(新建,删除[包含子文件[夹]],剪切,复制,重命名)
    C++ MFC 文件操作(新建,删除,剪切,复制,读数据,写数据,重命名)
    ip修改器
    Python大数据系列-01-关系数据库基本运算
    英语进阶系列-A05-英语升级练习三
    Python数据可视化系列-01-快速绘图
  • 原文地址:https://www.cnblogs.com/DF-yimeng/p/8893692.html
Copyright © 2011-2022 走看看