dinic 算法 基本思想及其模板

“网络流博大精深”—sideman语

一个基本的网络流问题
感谢WHD的大力支持

最早知道网络流的内容便是最大流问题,最大流问题很好理解:

解释一定要通俗!

如右图所示,有一个管道系统,节点{1,2,3,4},有向管道{A,B,C,D,E},即有向图一张. [1]是源点,有无限的水量,[4]是汇点,管道容量如图所示.试问[4]点最大可接收的水的流量?

这便是简单的最大流问题,显然[4]点的最大流量为50

死理性派请注意:流量是单位时间内的,总可以了吧!

然而对于复杂图的最大流方法是什么呢,有EK,Dinic,SAP,etc.下面介绍Dinic算法(看代码的直接点这)

Dinic 算法

Dinic算法的基本思路:
  1. 根据残量网络计算层次图。
  2. 在层次图中使用DFS进行增广直到不存在增广路
  3. 重复以上步骤直到无法增广

引自NOCOW,相当简单是吧…

小贴士:

一般情况下在Dinic算法中,我们只记录某一边的剩余流量.

  • 残量网络:包含反向弧的有向图,Dinic要循环的,每次修改过的图都是残量网络,
  • 层次图:分层图,以[从原点到某点的最短距离]分层的图,距离相等的为一层,(比如上图的分层为{1},{2,4},{3})
  • DFS:这个就不用说了吧…
  • 增广  :在现有流量基础上发现新的路径,扩大发现的最大流量(注意:增加量不一定是这条路径的流量,而是新的流量与上次流量之差)
  • 增广路:在现有流量基础上发现的新路径.(快来找茬,和上一条有何不同?)
  • 剩余流量:当一条边被增广之后(即它是增广路的一部分,或者说增广路通过这条边),这条边还能通过的流量.
  • 反向弧:我们在Dinic算法中,对于一条有向边,我们需要建立另一条反向边(弧),当正向(输入数据)边剩余流量减少I时,反向弧剩余流量增加I
Comzyh的较详细解释(流程) :

Dinic动画演示

  1. 用BFS建立分层图  注意:分层图是以当前图为基础建立的,所以要重复建立分层图
  2. 用DFS的方法寻找一条由源点到汇点的路径,获得这条路径的流量I 根据这条路径修改整个图,将所经之处正向边流量减少I,反向边流量增加I,注意I是非负数
  3. 重复步骤2,直到DFS找不到新的路径时,重复步骤1

注意(可以无视):

  • Dinic(其实其他的好多)算法中寻找到增广路后要将反向边增加I
  • Dinic中DFS时只在分层图中DFS,意思是说DFS的下一个节点的Dis(距源点的距离)要比自己的Dis大1,例如在图1中第一个次DFS中,1->2->4 这条路径是不合法的,因为Dis[2]=1;Dis[4]=1;
  • 步骤2中“获得这条路径的流量I “实现:DFS函数有参量low,代表从源点到现在最窄的(剩余流量最小)的边的剩余流量,当DFS到汇点是,Low便是我们要的流量I
对于反向弧(反向边)的理解:

这一段不理解也不是不可以,对于会写算法没什么帮助,如果你着急,直接无视即可.
先举一个例子(如右图):

必须使用反向弧的流网络

在这幅图中我们首先要增广1->2->4->6,这时可以获得一个容量为2的流,但是如果不建立4->2反向弧的话,则无
法进一步增广,最终答案为2,显然是不对的,然而如果建立了反向弧4->2,则第二次能进行
1->3->4->2->5->6的增广,最大流为3.

Comzyh对反向弧的理解可以说是”偷梁换柱“,请仔细阅读:
在上面的例子中,我们可以看出,最终结果是1->2->5->6和1->2->4->6和
1->3->4->6.当增广完1->2->4->5(代号A)后,在增广
1->3->4->2->5->6(代号B),相当于将经过节点2的A流从中截流1(总共是2)走2->5>6,而不走2->4>6了,同时B流也从节点4截流出1(总共是1)走4->6而不是4->2->5->6,相当于AB流做加法.

简单的说反向弧为今后提供反悔的机会,让前面不走这条路而走别的路.

Alwa同学非要我给他的文章加一个链接,大家可以看看他的文章: 有关网络流中的反向弧和增广路

Dinic算法的程序实现

最大流算法一直有一个入门经典题:POJ 1273 或者是UCACO 4_2_1 来自NOCOW(中文) 这两个是同一个题

给出这道题的代码

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;
int edge[300][300];//邻接矩阵
int dis[300];//距源点距离,分层图
int start,end;
int m,n;//N:点数;M,边数
int bfs(){
   memset(dis,-1,sizeof(dis));//以-1填充
   dis[1]=0;
   queue<int>q;
   q.push(start);
   while(!q.empty()){
        int u=q.front();
        q.pop();
        for(int i=1;i<=n;i++){
            if(dis[i]<0&&edge[u][i]){
                dis[i]=dis[u]+1;
                q.push(i);

            }
        }
   }
   if(dis[n]>0)
    return 1;
   else
    return 0;//汇点的DIS小于零,表明BFS不到汇点
}
//Find代表一次增广,函数返回本次增广的流量,返回0表示无法增广
int find(int x,int low){//Low是源点到现在最窄的(剩余流量最小)的边的剩余流量
    int a=0;
    if(x==n)
        return low;//是汇点
    for(int i=1;i<=n;i++){
        if(edge[x][i]>0&&dis[i]==dis[x]+1&&//联通,,是分层图的下一层
           (a=find(i,min(low,edge[x][i])))){//能到汇点(a <> 0)
            edge[x][i]-=a;
            edge[i][x]+=a;
            return a;
           }

    }
    return 0;
}
int main(){
   while(scanf("%d%d",&m,&n)!=EOF){
       memset(edge,0,sizeof(edge));
       for(int i=1;i<=m;i++){
          int u,v,w;
          scanf("%d%d%d",&u,&v,&w);
          edge[u][v]+=w;
       }
       start=1;
       end=n;
       int ans=0;
       while(bfs()){//要不停地建立分层图,如果BFS不到汇点才结束
        ans+=find(1,0x7fffffff);//一次BFS要不停地找增广路,直到找不到为止
       }
       printf("%d\n",ans);
   }
   return 0;
}
时间: 2024-10-06 10:06:44

dinic 算法 基本思想及其模板的相关文章

Dinic算法最大流入门

例题传送门 Dinic算法是网络流最大流的优化算法之一,每一步对原图进行分层,然后用DFS求增广路.时间复杂度是O(n^2*m),Dinic算法最多被分为n个阶段,每个阶段包括建层次网络和寻找增广路两部分. Dinic算法的思想是分阶段地在层次网络中增广.它与最短增广路算法不同之处是:最短增广路每个阶段执行完一次BFS增广后,要重新启动BFS从源点Vs开始寻找另一条增广路;而在Dinic算法中,只需一次BFS过程就可以实现多次增广. 简单来说,分为下面几步: 1.在剩余网络中查找是否存在从S到T

POJ 1815 - Friendship - [拆点最大流求最小点割集][暴力枚举求升序割点] - [Dinic算法模板 - 邻接矩阵型]

妖怪题目,做到现在:2017/8/19 - 1:41-- 不过想想还是值得的,至少邻接矩阵型的Dinic算法模板get√ 题目链接:http://poj.org/problem?id=1815 Time Limit: 2000MS Memory Limit: 20000K Description In modern society, each person has his own friends. Since all the people are very busy, they communic

POJ 3469.Dual Core CPU 最大流dinic算法模板

Dual Core CPU Time Limit: 15000MS   Memory Limit: 131072K Total Submissions: 24830   Accepted: 10756 Case Time Limit: 5000MS Description As more and more computers are equipped with dual core CPU, SetagLilb, the Chief Technology Officer of TinySoft C

POJ 1273 Drainage Ditches(网络流dinic算法模板)

POJ 1273给出M条边,N个点,求源点1到汇点N的最大流量. 本文主要就是附上dinic的模板,供以后参考. #include <iostream> #include <stdio.h> #include <algorithm> #include <queue> #include <string.h> /* POJ 1273 dinic算法模板 边是有向的,而且存在重边,且这里重边不是取MAX,而是累加和 */ using namespace

【模板】dinic算法

dinic算法用于解决最大流问题. 注意每次BFS之前把dist数组清空,源点的dist设为1. 1 #include<stdio.h> 2 #include<string.h> 3 #include<algorithm> 4 #define inf 1000000000 5 using namespace std; 6 int ans,tot,vert,edg,S,T,fr[100005],to[200005],nxt[200005],f[200005]; 7 int

图论4——探索网络流的足迹:Dinic算法

1. 网络流:定义与简析 1.1 网络流是什么? 网络流是一种"类比水流的解决问题方法,与线性规划密切相关"(语出百度百科). 其实,在信息学竞赛中,简单的网络流并不需要太高深的数学知识. 首先我们需要知道一些名词是什么意思: 点(\(node\)).就是一个节点.点集通常用\(V\)表示.其中,有一个源点\(s\)和一个汇点\(t\),所有的流都从源点\(s\)出发,经过一些边之后到达汇点\(t\). 边(\(edge\)).这个东西和大家在其他图论知识中所用到的差不多,用于连接两个

ISAP算法对 Dinic算法的改进

ISAP算法对 Dinic算法的改进: 在刘汝佳图论的开头引言里面,就指出了,算法的本身细节优化,是比较复杂的,这些高质量的图论算法是无数优秀算法设计师的智慧结晶. 如果一时半会理解不清楚,也是正常的.但是对于一个优秀的acmer来说,其算法的本身,可以锻炼你的思维.增长见识! 下面是我对 Dinic和ISAP的认识: Dinic算法比较值钱的 EK算法来说,已经有很大的提高了,其优势在哪里呢? 就是在于他的分层思想.在层次图上增广.但是,他也有弊端. 就是每次进行增广后,对于层次图都进行了从头

[POJ 1273]Drainage Ditches(Edmond-Krap算法和Dinic算法求最大流)

自NOIP 2014结束之后将近一个星期没撸题了,现在开始搞省选,发个水水的裸网络流题解吧. 题目链接:http://poj.org/problem?id=1273 裸网络流,模板题. 1.Edmond_Karp算法 #include <iostream> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <algorithm> #include <que

dinic算法学习(以poj1273为例)

Dinic 算法模板 Dinic算法是一种比较容易实现的,相对比较快的最大流算法. 求最大流的本质,就是不停的寻找增广路径.直到找不到增广路径为止. 对于这个一般性的过程,Dinic算法的优化如下: (1)Dinic算法首先对图进行一次BFS,然后在BFS生成的层次图中进行多次DFS. 层次图的意思就是,只有在BFS树中深度相差1的节点才是连接的. 这就切断了原有的图中的许多不必要的连接.很牛逼! 这是需要证明的,估计证明也很复杂. (2)除此之外,每次DFS完后,会找到路径中容量最小的一条边.