日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

dinic (最大流) 算法 讲解

發(fā)布時間:2025/3/15 编程问答 18 豆豆
生活随笔 收集整理的這篇文章主要介紹了 dinic (最大流) 算法 讲解 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

?

網(wǎng)絡流入門—用于最大流的Dinic算法

轉自:http://comzyh.tk/blog/archives/568/

“網(wǎng)絡流博大精深”—sideman語

一個基本的網(wǎng)絡流問題


感謝WHD的大力支持

?

最早知道網(wǎng)絡流的內容便是最大流問題,最大流問題很好理解:

解釋一定要通俗!

如右圖所示,有一個管道系統(tǒng),節(jié)點{1,2,3,4},有向管道{A,B,C,D,E},即有向圖一張. [1]是源點,有無限的水量,[4]是匯點,管道容量如圖所示.試問[4]點最大可接收的水的流量?

這便是簡單的最大流問題,顯然[4]點的最大流量為50

死理性派請注意:流量是單位時間內的,總可以了吧!

然而對于復雜圖的最大流方法是什么呢,有EK,Dinic,SAP,etc.下面介紹Dinic算法(看代碼的直接點這)

Dinic 算法

Dinic算法的基本思路:
  • 根據(jù)殘量網(wǎng)絡計算層次圖。
  • 在層次圖中使用DFS進行增廣直到不存在增廣路
  • 重復以上步驟直到無法增廣
  • 引自NOCOW,相當簡單是吧...

    小貼士:

    一般情況下在Dinic算法中,我們只記錄某一邊的剩余流量.

    • 殘量網(wǎng)絡:包含反向弧的有向圖,Dinic要循環(huán)的,每次修改過的圖都是殘量網(wǎng)絡,
    • 層次圖:分層圖,以[從原點到某點的最短距離]分層的圖,距離相等的為一層,(比如上圖的分層為{1},{2,4},{3})
    • DFS:這個就不用說了吧...
    • 增廣? :在現(xiàn)有流量基礎上發(fā)現(xiàn)新的路徑,擴大發(fā)現(xiàn)的最大流量(注意:增加量不一定是這條路徑的流量,而是新的流量與上次流量之差)
    • 增廣路:在現(xiàn)有流量基礎上發(fā)現(xiàn)的新路徑.(快來找茬,和上一條有何不同?)
    • 剩余流量:當一條邊被增廣之后(即它是增廣路的一部分,或者說增廣路通過這條邊),這條邊還能通過的流量.
    • 反向弧:我們在Dinic算法中,對于一條有向邊,我們需要建立另一條反向邊(弧),當正向(輸入數(shù)據(jù))邊剩余流量減少I時,反向弧剩余流量增加I
    Comzyh的較詳細解釋(流程) :


    Dinic動畫演示

  • 用BFS建立分層圖? 注意:分層圖是以當前圖為基礎建立的,所以要重復建立分層圖
  • 用DFS的方法尋找一條由源點到匯點的路徑,獲得這條路徑的流量I 根據(jù)這條路徑修改整個圖,將所經(jīng)之處正向邊流量減少I,反向邊流量增加I,注意I是非負數(shù)
  • 重復步驟2,直到DFS找不到新的路徑時,重復步驟1
  • 注意(可以無視):

    • Dinic(其實其他的好多)算法中尋找到增廣路后要將反向邊增加I
    • Dinic中DFS時只在分層圖中DFS,意思是說DFS的下一個節(jié)點的Dis(距源點的距離)要比自己的Dis大1,例如在圖1中第一個次DFS中,1->2->4 這條路徑是不合法的,因為Dis[2]=1;Dis[4]=1;
    • 步驟2中"獲得這條路徑的流量I "實現(xiàn):DFS函數(shù)有參量low,代表從源點到現(xiàn)在最窄的(剩余流量最小)的邊的剩余流量,當DFS到匯點是,Low便是我們要的流量I
    對于反向弧(反向邊)的理解:

    這一段不理解也不是不可以,對于會寫算法沒什么幫助,如果你著急,直接無視即可.
    先舉一個例子(如右圖):

    ?

    必須使用反向弧的流網(wǎng)絡

    在這幅圖中我們首先要增廣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->6(代號A)后,在增廣 1->3->4->2->5->6(代號B),相當于將經(jīng)過節(jié)點2的A流從中截流1(總共是2)走2->5>6,而不走2->4>6了,同時B流也從節(jié)點4截流出1(總共是1)走4->6而不是4->2->5->6,相當于AB流做加法.

    ?

    簡單的說反向弧為今后提供反悔的機會,讓前面不走這條路而走別的路.

    Dinic算法的程序實現(xiàn)

    最大流算法一直有一個入門經(jīng)典題:POJ 1273 或者是UCACO 4_2_1 來自NOCOW(中文) 這兩個是同一個題

    給出這道題的代碼

    ?

    ?1?#include<cstdio>
    ?2?#include<cstring>
    ?3?#include<cmath>
    ?4?#include<iostream>
    ?5?#include<algorithm>
    ?6?#include<set>
    ?7?#include<map>
    ?8?#include<queue>
    ?9?#include<vector>
    10?#include<string>
    11?#define?Min(a,b)?a<b?a:b
    12?#define?Max(a,b)?a>b?a:b
    13?#define?CL(a,num)?memset(a,num,sizeof(a));
    14?#define?eps??1e-12
    15?#define?inf???0x7fffffff
    16?
    17?//freopen("data.txt","r",stdin);
    18?const?double?pi??=?acos(-1.0);
    19?typedef???__int64??ll;
    20?const?int?maxn?=?300?;
    21?using?namespace?std;
    22?int?n?,?m;
    23?int?flow[maxn][maxn],dis[maxn]?;//dis[i],表示??到?原點??s?的?層數(shù)?
    24???
    25?int?bfs()//?重新?建?圖?(按?層數(shù)?建圖)
    26?{
    27?????CL(dis,-1);
    28?????dis[1]?=?0?;
    29?????queue<int>que;
    30?????que.push(1);
    31?????while(!que.empty())
    32?????{
    33?????????int??k?=?que.front();que.pop()?;
    34?????????for(?int?i?=?1;i<=?n;i++)
    35?????????{
    36?????????????if(flow[k][i]?>?0?&&?dis[i]?<?0?)//?如果?可以??可以到達?但?還沒有?訪問
    37?????????????{
    38?????????????????dis[i]?=?dis[k]+?1?;
    39?????????????????que.push(i)?;
    40?????????????}
    41?????????}
    42?????}
    43?
    44?????if(dis[n]?>?0)?return?1;
    45?????else?return??0?;
    46?
    47?}
    48?int??dfs(int?x,int?mx)//?查找??路徑上的?最小?的?流量
    49?{
    50?
    51?????int?i?,?a?;
    52?????if(x?==?n)?return?mx?;
    53?
    54?????for(i?=?1;i<=?n;i++)
    55?????{
    56?????????if(flow[x][i]?>?0?&&?dis[i]?==?dis[x]?+?1??&&?(a?=dfs(i,min(mx,flow[x][i]))))
    57?????????{
    58?????????????flow[x][i]?-=?a;
    59?????????????flow[i][x]?+=?a;
    60?????????????return?a?;
    61?
    62?
    63?????????}
    64?????}
    65?????return?0?;
    66?}
    67?int?main()
    68?{
    69?????//freopen("data.txt","r",stdin);
    70?????int?i?,s,e,c;
    71?????while(scanf("%d%d",&m,&n)!=EOF)
    72?????{
    73?????????CL(flow,0);
    74?????????for(i?=?0??;?i?<?m;i++)
    75?????????{
    76?????????????scanf("%d%d%d",&s,&e,&c);
    77?????????????flow[s][e]?+=?c;
    78?????????}
    79???????int?ans?=?0;
    80???????int?res;
    81?
    82???????while(bfs())
    83???????{
    84?
    85?
    86??????????while(res?=?dfs(1,inf))?ans+=?res?;
    87?
    88???????}
    89???????printf("%d\n",ans);
    90?????}
    91?
    92?}

    ?更高效的 dinic

    ?

    http://acm.hdu.edu.cn/showproblem.php?pid=4292

    hdu 4292 ? Food?

    ?

    ?

    ?

    #include<cstdio>
    ??#include<cstring>
    ??#include<cmath>
    ??#include<iostream>
    ??#include<algorithm>
    ??#include<set>
    ??#include<map>
    ??#include<queue>
    ??#include<vector>
    ??#include<string>
    ?#define?INF?0x3fffffff
    ?#define?F(x)?(x)
    ?#define?N(x)?(205+(x))
    ?#define?CPN(x)?(410+(x))
    ?#define?D(x)?(615+(x))
    ?#define?maxn?250
    ?#define?CL(a,b)?memset(a,b,sizeof(a))
    ?#define?Pnum?210
    ?using?namespace?std;
    ?
    ??int?next[maxn*20],dis[maxn*10];
    ??int?s,e;
    ??int??n,?fnum?,dnum?,f[maxn],d[maxn],cnt;
    ??struct?node
    ??{
    ??????int?to;
    ??????int?cap?;
    ??????int?next?;
    ??}p[200000]?;
    ???int?que[maxn*maxn]?,idx;
    ?
    ??void?add(int?x,int?y,int?cap)// 建邊 注意 反向邊的流量為 0
    ??{
    ??????p[cnt].to?=?y;
    ??????p[cnt].cap?=?cap?;
    ??????p[cnt].next?=?next[x];
    ??????next[x]?=?cnt++?;
    ?
    ??????p[cnt].to?=?x;
    ??????p[cnt].cap?=?0;
    ??????p[cnt].next?=?next[y];
    ??????next[y]?=?cnt++?;
    ??}
    ??int?bfs()//?重新?建?圖?(按?層數(shù)?建圖)
    ??{
    ?
    ??????memset(dis,0xff,sizeof(dis))?;
    ??????dis[s]?=?0?;
    ??????queue<int>que;
    ??????que.push(s);
    ?
    ??????while(!que.empty())
    ??????{
    ?
    ??????????int??k?=?que.front();que.pop()?;
    ??????????for(?int?i?=?next[k];i!=-1;i?=?p[i].next)
    ??????????{
    ??????????????int?v?=?p[i].to;
    ?
    ?
    ??????????????int?cap?=?p[i].cap?;
    ?
    ??????????????if(cap?>?0?&&?dis[v]?<?0?)//?如果?可以??可以到達?但?還沒有?訪問
    ??????????????{
    ?
    ??????????????????dis[v]?=?dis[k]+?1?;
    ??????????????????que.push(v)?;
    ??????????????}
    ??????????}
    ?
    ??????}
    ?
    ?
    ??????if(dis[e]?>?0)?return?1;
    ??????else?return??0?;
    ?
    ??}
    ?
    ??
    ?
    ??int??dfs(int?x,int?mx)//?查找??路徑上的?最小?的?流量
    ??{
    ?
    ??????int?i?,?a?,tf?=??0;
    ?
    ??????if(x?==?e)?return?mx?;
    ?
    ??????for(i?=?next[x];i!=?-?1;i?=?p[i].next)
    ??????{
    ??????????int?v?=?p[i].to?;
    ??????????int?cap?=?p[i].cap?;
    ??????????if(cap?>?0?&&?dis[v]?==?dis[x]?+?1??&&?(a?=dfs(v,min(cap,mx))))
    ??????????{
    ?
    ??????????????p[i].cap?-=?a;
    ??????????????p[i^1].cap?+=?a;
    ?
    ????????????????return?a;
    ?
    ?
    ??????????}
    ??????}
    ??????if(!tf)?dis[x]?=?-1;//?沒有?找到?最小流量?,說明?從這個點到不了?終點?,所以??標記一下
    ??????return?tf?;
    ??}
    ?
    ?
    ?
    ?
    ??
    ??int?main()
    ??{
    ?????int?i?,?j?;
    ?????char?c[250]?;
    ??????//freopen("data.txt","r",stdin)?;
    ?????while(scanf("%d%d%d",&n,&fnum,&dnum)!=EOF)
    ?????{
    ?
    ?????????CL(next,-1)?;
    ?????????cnt?=?0;
    ??????????s?=?0;
    ??????????e?=?2000;
    ??????????for(i?=?1?;?i?<=?fnum;i++)
    ??????????{
    ??????????????scanf("%d",&f[i]);
    ??????????}
    ??????????for(i?=?1?;?i<=?dnum;i++)
    ??????????{
    ??????????????scanf("%d",&d[i])?;
    ??????????}
    ??????????for(i?=?1;?i?<=?n;i++)//??人?和?吃的
    ??????????{
    ??????????????scanf("%s",c);
    ??????????????for(j?=?0?;??j<?fnum?;j++)
    ??????????????{
    ??????????????????if(c[j]?==?'Y')
    ??????????????????{
    ?
    ??????????????????????add(j?+?1,i?+?Pnum,1)?;
    ??????????????????}
    ??????????????}
    ?
    ??????????}
    ??????????for(i?=?1;?i<=?n;i++)//??人?和?喝的
    ??????????{
    ??????????????scanf("%s",c);
    ??????????????for(j?=?0?;??j<?dnum?;j++)
    ??????????????{
    ??????????????????if(c[j]?==?'Y')
    ??????????????????{
    ?
    ??????????????????????add(i?+?Pnum*2,j?+?Pnum*3?+?1,1)?;
    ??????????????????}
    ??????????????}
    ?
    ??????????}
    ??????????for(i?=?1;?i?<=?fnum;i++)//增加源點
    ??????????{
    ?
    ??????????????add(0,i,f[i])?;
    ??????????}
    ??????????for(i?=?Pnum*3??+?1,j?=?1;?j?<=?dnum;i++,j++)//增加?匯點
    ??????????{
    ?
    ??????????????add(i,e,d[j])?;
    ?
    ??????????}
    ?
    ??????????for(i?=?1;?i?<=?n;i++)//??將人?拆分
    ??????????{
    ?
    ??????????????add(i?+?Pnum,i?+Pnum*2,1);
    ??????????}
    ????????????int?ans?=?0;
    ????????int?res;
    ?
    ????????while(bfs())
    ????????{
    ?
    ?
    ???????????while(res?=?dfs(s,INF))?ans+=?res?;
    ?
    ????????}
    ????????printf("%d\n",ans);
    ?????}
    ??}

    ?

    轉載于:https://www.cnblogs.com/acSzz/archive/2012/09/13/2683820.html

    總結

    以上是生活随笔為你收集整理的dinic (最大流) 算法 讲解的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。