dinic (最大流) 算法 讲解
?
網絡流入門—用于最大流的Dinic算法
轉自:http://comzyh.tk/blog/archives/568/
“網絡流博大精深”—sideman語
一個基本的網絡流問題
感謝WHD的大力支持
?
最早知道網絡流的內容便是最大流問題,最大流問題很好理解:
解釋一定要通俗!
如右圖所示,有一個管道系統,節點{1,2,3,4},有向管道{A,B,C,D,E},即有向圖一張. [1]是源點,有無限的水量,[4]是匯點,管道容量如圖所示.試問[4]點最大可接收的水的流量?
這便是簡單的最大流問題,顯然[4]點的最大流量為50
死理性派請注意:流量是單位時間內的,總可以了吧!
然而對于復雜圖的最大流方法是什么呢,有EK,Dinic,SAP,etc.下面介紹Dinic算法(看代碼的直接點這)
Dinic 算法
Dinic算法的基本思路:
引自NOCOW,相當簡單是吧...
小貼士:
一般情況下在Dinic算法中,我們只記錄某一邊的剩余流量.
- 殘量網絡:包含反向弧的有向圖,Dinic要循環的,每次修改過的圖都是殘量網絡,
- 層次圖:分層圖,以[從原點到某點的最短距離]分層的圖,距離相等的為一層,(比如上圖的分層為{1},{2,4},{3})
- DFS:這個就不用說了吧...
- 增廣? :在現有流量基礎上發現新的路徑,擴大發現的最大流量(注意:增加量不一定是這條路徑的流量,而是新的流量與上次流量之差)
- 增廣路:在現有流量基礎上發現的新路徑.(快來找茬,和上一條有何不同?)
- 剩余流量:當一條邊被增廣之后(即它是增廣路的一部分,或者說增廣路通過這條邊),這條邊還能通過的流量.
- 反向弧:我們在Dinic算法中,對于一條有向邊,我們需要建立另一條反向邊(弧),當正向(輸入數據)邊剩余流量減少I時,反向弧剩余流量增加I
Comzyh的較詳細解釋(流程) :
Dinic動畫演示
注意(可以無視):
- 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->6(代號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流做加法.
?
簡單的說反向弧為今后提供反悔的機會,讓前面不走這條路而走別的路.
Dinic算法的程序實現
最大流算法一直有一個入門經典題: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?的?層數?
24???
25?int?bfs()//?重新?建?圖?(按?層數?建圖)
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=4292hdu 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()//?重新?建?圖?(按?層數?建圖)
??{
?
??????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 (最大流) 算法 讲解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 扩展Linux分区的一种方法
- 下一篇: 性能测试分析