网络流-最大流-Dinic算法

  1. 源:只出不进,叫做源点。
  2. 汇点:只进不出,叫做汇点。
  3. 每条边可以通过的最大量称作容量MAXV
  4. 每条边实际通过的量称作流量F
  5. 每条边容量-流量称作残量 (表示这条边距最大承受的量的数量)
  6. 一条完整的路距最大的承受量取决于这条路中的最小流量(木桶效应)

最大流

定义

  • 给定指定的一个有向图,其中有两个特殊的点S(Sources)T(Sinks),每条边有指定的容量(Capacity),求满足条件的从ST的最大流(MaxFlow).

通俗点来讲就是:
你家是汇,自来水厂是源,然后自来水厂和你家之间修了很多条水管子接在一起,水管子规格不一,有的容量大,有的容量小,然后问自来水厂开闸放水,你家收到水的最大流量是多少。
假如在自来水厂可以非常满的灌满每一条水管,但是由于水管的容量不一样所以能通到家中的最大流量肯定不是水管容量的总和。那么最大流是多少呢?

如图



其中1号是源点4汇点。每条边的容量都是1
我们可以一眼看出图中的1到4的最大流是2,通过路径(1,2,4)(1,3,4),最大流为2

然后对于程序,我们先引入一个叫做残量图的概念:
顾名思义,残嘛,就是剩的意思,即剩下的量,我们把一条边的最大容量MAXV和其实际的流量F的差值叫做残量,即
残量=MAXV−F

然后我们将残量作为每一条边的权值,构建一个图就叫做残量图,若权值为0,那么就相当于一条断边
此时,假设我们从出发进行的某一次dfs到了,那么就说明这条路线的流量还可以增加,具体增加的量就被这条路线上的流量最小的那条边决定了,我们把这样的路叫做增广路

(增广路就是一条从源点到汇点的路,并且带有一个值,表示该增广路的最大流量,该值得大小取决于该增广路中拥有最小流量的边。)

就像上图,我们知道(1,2,3,4)这条路线是可以在增大流量的,且最大可增大的流量是1,故我们就将其经过的边的容量-1得到了这个图的残量图

然后我们再dfs,却发现不能到达了,于是程序这个时候就返回此时的最大流1
但是并不是这样的啊??

这个时候我们发现,如果程序在dfs2的时候若可以向4dfs的话就不会出错了啊!那么这个时候,如果我们给这个图上的每一个点都标上深度的话,我们dfs的时候只允许从低深度往高深度走的话,岂不是可以大幅度避免出错呢?于是这个时候dinic算法的思想就出来了,就是不断用bfs标深度然后不断dfs直到不能到达为止


但是仅仅通过标记深度能不能完全解决问题呢?答案是不能的,即使它可以大幅度减少

那如果说我们可以让程序在dfs到3的时候发现问题并后悔不就ok了吗?

于是有一种最easy的方法就是引入一个反向边的概念,即每一条边(u,v)都有一条反向边(v,u),且这两条边的最大容量相等,一对边的实际流量之和等于最大流量(容量),即F(u,v)+F(v,u)=MAXV(v,u)=MAXV(u,v)
假如一条边的流量+a时,反向边就-a.

于是就有


然后根据dfs,我们找到了增广路(1,2,3,4),然后图就该变成这样

然后我们继续dfs,把反向边也当作可走的边dfs,然后得到了增广路(1,3,2,4)
然后图就成了这样

最大流为2
仔细观察可以发现,上图其实和我们直接dfs(1,2,4)(1,3,4)得到的结果一样!!这也就正确了!
其实奥妙在第2个dfs

当程序将边(3,2)的流量加一,(2,3)的流量减一时,其实就相当于把边(2,3)的流量给退回去(不信你看退回后的(2,3)和原图的(2,3)是不是一样的),然后还把本来属于路径(1,2,3,4)的流量“交付”给了(1,3),于是就有了两条路(1,2,4)(1,3,4)
这就是其奥妙所在
于是这个算法框架就此浮出水面:
先标深度再用dfs找一次增广路然后再bfs标深度在dfs然后bfs,dfs,bfs,dfs,bfs,dfs,bfs,dfs…直到bfs时发现断层,说明此时已经找到了最大流

Dinic算法
就是应用bfs构造层次图,然后通过dfs来增广.增广过程除了当前节点x外,还需要传入一个表示“目前为止所有边的最小残量”的a,当前节点x为汇点或者a==0时终止dfs过程,否则会多路增广。还有在dfs过程中的一个重要优化:保存每个节点x正在遍历的子边保存在cur[x].以免重复计算遍历过的子边.

cur数组原理是什么呢?其实就是当我们在对一个节点u(假设有6儿子)进行增广时,把他的儿子1,2,3,4的可用流量都用完了,那么在下一次dfs模块走到u时,我们如果可以直接从儿子5开始进行增广就可以大大减少额外的时间开销

cur数组实现:我们可以在dfs里面做一点改动,即先定义一个cur数组,在dfs之前把邻接表的head数组拷贝到cur里面,然后在遍历u的时候同时把cur里面的边的下标后移,以达到将用完的边略去的目的

P3376 【模板】网络最大流

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int M=2*100000+10;
int head[M],cnt=0;
int dis[M],cur[M];
int n,m,s,t;
struct node
{
    int v,w,nxt;
} edge[M];
void add(int x,int y,int w)
{
    
    edge[cnt].nxt=head[x];//边的编号一定要从0开始,因为0^1=1,1^1=0;2^1=3,3^1=2
    edge[cnt].v=y;
    edge[cnt].w=w;
    head[x]=cnt;
    cnt++;
}
void dataset( )
{
    memset(head,-1,sizeof(head));
    scanf("%d%d%d%d",&n,&m,&s,&t);
    int a,b,c;
    for(int i=1; i<=m; i++)
    {
        scanf("%d%d%d",&a,&b,&c);
        add(a,b,c);//正向边初始值为容量
        add(b,a,0);//反向边初始值为0
    }
}
bool bfs(int s)
{
    memset(dis,-1,sizeof(dis));
    dis[s]=0;
    queue<int>qu;
    qu.push(s);
    while(!qu.empty( ))
    {
        int u=qu.front( );
        qu.pop( );
        for(int i=head[u]; i!=-1; i=edge[i].nxt)
        {
            int v=edge[i].v;
            if(dis[v]==-1&&edge[i].w)
            {
                dis[v]=dis[u]+1;
                qu.push(v);
            }
        }
    }
    return dis[t]!=-1;
}
int dfs(int u,int flo)
{
    if(u==t)
    {
        return flo;
    }
    int detla=flo;
    for(int i=cur[u]; i!=-1; i=edge[i].nxt)
    {
        cur[u]=edge[i].nxt;
        int v=edge[i].v;
        if(dis[v]==dis[u]+1&&edge[i].w>0)
        {
            int d=dfs(v,min(detla,edge[i].w));
            edge[i].w-=d;
            edge[i^1].w+=d;//反向边
            detla-=d;
            if(detla==0)
            {
                break;
            }
        }
    }
    return flo-detla;
}
int dini( )
{
    int ans=0;
    while(bfs(s))
    {
        for(int i=1; i<=n; i++)
        {
            cur[i]=head[i];
        }
        ans+=dfs(s,INF);
    }
    return ans;
}
int main( )
{
    dataset( );
    printf("%d\n",dini( ));
    return 0;
}

\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \本文参考博客
\ \ \ \ \ \ \ \ \ \https://blog.csdn.net/weixin_43907802/article/details/84705855

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容

  • 最大流&&最小费用最大流&&最大二分匹配 中文是2017年8月的笔记,英文是2018.11月的笔记 英文笔记来自于...
    廖少少阅读 34,521评论 6 20
  • 概述 在最大流问题中,我们希望在不违反任何容量限制的情况下,计算出从源节点运送物料到汇点的最大速率 流网络 流网络...
    琦思妙想君阅读 2,099评论 0 2
  • 题目描述 如题,给出一个网络图,以及其源点和汇点,求出其网络最大流。 输入输出格式 输入格式第一行包含四个正整数N...
    Ricardo_Y_Li阅读 7,738评论 2 4
  • 目录 1.流网络及最大流问题1.1 流网络1.2 最大流问题1.3 最大流问题的三种解法对比 2.Ford-Ful...
    王侦阅读 4,574评论 0 3
  • 摘要:最大流可以看做是把一些东西从源点s送到汇点t,可以从其他的点中转,每条边最多只能输送一定的物品,求最多可以把...
    暖夏未眠丶阅读 1,883评论 0 3