warshall算法求传递闭包c++_【建模小课堂】图论算法
圖論算法
圖論算法在計算機科學中扮演著很重要的角色,它提供了對很多問題都有效的一種簡單而系統的建模方式。很多問題都可以轉化為圖論問題,然后用圖論的基本算法加以解決。這類問題算法主要包括Dijkstra,Floyd,Prim,最短路、網絡流、二分圖等算法。
?背景?
哥尼斯堡(KOnigsberg)七橋問題
在哥尼斯堡的一個公園里,有七座橋將河中兩個島及島與河岸連接起來,問是否可能從這四塊陸地中任一塊出發,恰好通過每座橋一次,再回到起點。
1736年,在經過一年的研究之后,29歲的歐拉提交了《哥尼斯堡七橋》的論文,圓滿解決了這一問題,同時開創了數學新一分支---圖論。
七橋問題在論文中,歐拉將七橋問題抽象出來,把每一塊陸地考慮成一個點,連接兩塊陸地的橋以線表示。并由此得到了如圖一樣的幾何圖形。?若我們分別用A、B、C、D四個點表示為哥尼斯堡的四個區域。這樣著名的“七橋問題”便轉化為是否能夠用一筆不重復地畫出過此七條線的問題了。若可以畫出來,則圖形中必有終點和起點,并且起點和終點應該是同一點,由于對稱性可知由B或C為起點得到的效果是一樣的,若假設以A為起點和終點,則必有一離開線和對應的進入線,若我們定義進入A的線的條數為入度,離開線的條數為出度,與A有關的線的條數為A的度,則A的出度和入度是相等的,即A的度應該為偶數。即要使得從A出發有解則A的度數應該為偶數,而實際上A的度數是5為奇數,于是可知從A出發是無解的。同時若從B或D出發,由于B、D的度數分別是3、3,都是奇數,即以之為起點都是無解的。由上述理由可知,對于所抽象出的數學問題是無解的,即“七橋問題”也是無解的。
由此我們得到:歐拉回路關系
(對于一個連通圖,通常把從某結點出發一筆畫成所經過的路線叫做歐拉路。人們又通常把一筆畫成回到出發點的歐拉路叫做歐拉回路。具有歐拉回路的圖叫做歐拉圖。)
可知要使得一個圖形可以一筆畫,必須滿足如下兩個條件:
1. 圖形必須是連通的。
2. 圖中的“奇點”個數是0或2。
?引進概念?
圖:
二元組G(V,E)稱為圖(graph)。V為頂點(vertex) 或結點(node)的集。E為圖中邊的集合。
?點用數字0...n-1表示
?點對(u,v)稱為邊(edge)或稱弧
?子圖(subgraph):邊的子集和相關聯的點集
?帶權圖:可以給邊加權(weight),成為帶權圖,或加權圖(weightedgraph).權通常代表費用、距離等,可以是正數,也可以是負數
?有向圖:邊都是單向(unidirectional)的,因此邊(u,v)是有序數對.(最基本的圖通常被定義為“無向圖”,與之對應的則被稱為“有向圖”。兩者的區別在于,有向圖中的邊是有方向性的。下圖即是一個有向圖,邊的方向分別是:(1->2), (1-> 3), (3-> 1), (1->5), (2->3), (3->4), (3->5), (4->5), (1->6), (4->6)。
注意,圖中的邊(1->3)和(3->1)是不同的。有向圖和無向圖的許多原理和算法是相通的。)
有時用弧(arc)專指有向邊?
有時用弧(arc)專指有向邊
?一條路徑(path)是一個結點序列,路上的相鄰結點在圖上是鄰接的
?如果結點和邊都不重復出現,則稱為簡單路徑(simplepath).如果除了起點和終點相同外沒有重復頂點和邊,稱為圈(cycle).
?不相交路(disjoint path)表示沒有除了起點和終點以外的公共點的路.更嚴格的全圖:N個頂點的圖,有N(N-1)/2個節點于(u,v),若鄰接則改為非鄰接,若非鄰接則改為鄰接,得到的圖原圖的補圖
生成樹:
?樹:N個點,N-1條邊的連通圖(無環連通圖)
?生成樹:包含某圖G所有點的樹
?一個圖G是樹當且僅當以下任意一個條件成立
? ?G有V-1條邊,無圈
? ?G有V-1條邊,連通
? ?任意兩點只有唯一的簡單路徑
? ?G連通,但任意刪除一條邊后不連通
?最短路問題?
對一幅圖G,我們對每一條邊賦權w(e),成為一個賦權圖。H是G的一個子圖,則W(H)?= sigma(w(e)),也就是對每條邊的權求和。尋找從一個點a到另一個b的一個子圖,使得權和最小,即為最短路問題。
解法一:Dijkstra算法(僅適用于無負權邊的情況)
把頂點集合V分成兩組:?(1)S:已求出的頂點的集合(初始時只含有源點VO)?(2)V-S=T:尚未確定的頂點集合
將T中頂點按遞增的次序加入到S中,保證:
?(1)從源點VO到S中其他各頂點的長度都不大于從VO到T中任何頂點的最短路徑長度
?(2)每個頂點對應一個距離值
?S中頂點距離:從VO到此頂點的最短距離
?T中頂點距離:從V0到此頂點的只包括S中頂點作中間頂點的
算法圖解:
不斷求一個點集合中的每個點,和與他相鄰點最短路的最小值。我們還是從實例出發,更容易講解。求下面這個圖從A到L的最短路。
(1)
令a1 = A(便于標記),t(a1)?=?0(表示點a1到a的最短路),S={a1}(被選擇的點的集合),T =?0(空集?表示被選擇的最短路的邊集)。
(2)
求與S中的點a1與他相鄰的點的距離d,取點a2 = C使得距離最小。令S={a1,a2},T={AC}。
(3)
重復第二步,求S中的點a1,a2的相鄰點中(去除已選擇點),距離最小的那個,則為AB,CE。再取AB,CE中權和最小的一個,B,所以a3=B,令S={a1,a2,a3},T={AC,AB}。
…
持續下去,不斷尋找,S集合中的每個點與他相鄰點的最小距離,然后在這些最小距離中找到最小的那個加入到S中,同時加入相應的邊。
直到到達終點L為止。最后得到的最短路為:
此算法是多項式時間可求解的。
解法二:Floyd算法(插點法)
Floyd-Warshall算法(Floyd-Warshall algorithm)是解決任意兩點間的最短路徑的一種算法,可以正確處理有向圖或負權的最短路徑問題。
算法思想原理:
Floyd算法是一個經典的動態規劃算法。用通俗的語言來描述的話,首先我們的目標是尋找從點i到點j的最短路徑。從動態規劃的角度看問題,我們需要為這個目標重新做一個詮釋。從任意節點i到任意節點j的最短路徑不外乎2種可能,1是直接從i到j,2是從i經過若干個節點k到j。所以,我們假設Dis(i,j)為節點u到節點v的最短路徑的距離,對于每一個節點k,我們檢查Dis(i,k)?+ Dis(k,j)?< Dis(i,j)是否成立,如果成立,證明從i到k再到j的路徑比i直接到j的路徑短,我們便設置Dis(i,j)?= Dis(i,k)?+ Dis(k,j),這樣一來,當我們遍歷完所有節點k,Dis(i,j)中記錄的便是i到j的最短路徑的距離。
算法描述:
暑假,小哼準備去一些城市旅游。有些城市之間有公路,有些城市之間則沒有,如左圖。為了節省經費以及方便計劃旅程,小哼希望在出發之前知道任意兩個城市之前的最短路程。
上圖中有4個城市8條公路,公路上的數字表示這條公路的長短。請注意這些公路是單向的。我們現在需要求任意兩個城市之間的最短路程,也就是求任意兩個點之間的最短路徑。我們可以用一個4*4的矩陣(二維數組e)來存儲。比如1號城市到2號城市的路程為2,則設e[1][2]的值為2。2號城市無法到達4號城市,則設置e[2][4]的值為∞。另外此處約定一個城市自己到自己的路程也是0,例如e[1][1]為0。
如果求任意兩點之間的最短路徑,兩點之間可以直接到達但卻不是最短的路徑,要讓任意兩點(例如從頂點a點到頂點b)之間的路程變短,只能引入第三個點(頂點k),并通過這個頂點k中轉即a->k->b,才可能縮短原來從頂點a點到頂點b的路程。每個頂點都有可能使得另外兩個頂點之間的路程變短。
當任意兩點之間不允許經過第三個點時,這些城市之間最短路程就是初始路程。
在只允許經過1號頂點的情況下,任意兩點之間的最短路程更新為:
在只允許經過1號頂點的情況下,只需判斷e[i][1]+e[1][j]是否比e[i][j]要小即可。e[i][j]表示的是從i號頂點到j號頂點之間的路程。e[i][1]+e[1][j]表示的是從i號頂點先到1號頂點,再從1號頂點到j號頂點的路程之和。其中i是1~n循環,j也是1~n循環接下來繼續求在只允許經過1和2號兩個頂點的情況下任意兩點之間的最短路程。
我們需要在只允許經過1號頂點時任意兩點的最短路程的結果下,再判斷如果經過2號頂點是否可以使得i號頂點到j號頂點之間的路程變得更短。即判斷e[i][2]+e[2][j]是否比e[i][j]要小,在只允許經過1和2號頂點的情況下,任意兩點之間的最短路程更新為:
判斷e[i][2]+e[2][j]是否比e[i][j]要小。同理,繼續在只允許經過1、2和3號頂點進行中轉的情況下,求任意兩點之間的最短路程。任意兩點之間的最短路程更新為:
最后允許通過所有頂點作為中轉,任意兩點之間最終的最短路程為:
?最小生成樹問題?
同樣在一個連通賦權圖中,尋找一顆生成樹使得權和最小。
Kruskal算法(避圈法):
(1)在G中選取最小權邊e,并置邊數i=1;
(2)當i=n-1時結束,否則轉向(3);
(3設已選擇的邊為e,e2,.,e,在G中選不同于e,e2,.,e的邊e;+1,使得e;+是滿足與e,e,...,e不構成回路且權值最小的邊
(4)置為i+1,轉向(2)。
不斷尋找最小權的邊即可。
比如,尋找下圖的最小生成樹
結果如下
?網絡流問題?
如下圖所示,有一個頂點,稱為源點(source);還有一個頂點,稱為匯點(sink)。對于頂點,它最大流出2,因此它的最大流入為2,如下右圖所示。而他的最大流也就是5。
要想計算最大流,同樣可是使用前面的思想——分階段進行。令開始時所有邊都沒有流,如下中間圖所示。我們可以用殘余圖(residual graph)來表示對于每條邊還能再添加上多少流。對于每一條邊,可以從容量中減去當前的流而計算出殘留的流。
第一步:假設我們選擇路徑,此時會發出2個單位的流通過這條路徑的每一邊,如下中間圖所示。對比左圖,我們做如下約定:一旦注滿(使飽和)一條邊,例如到和到,就將這條邊從殘余圖(也就是中間圖)去掉,如下右圖所示。
第二步:接下來選擇路徑,此時也會發出2個單位的流通過這條路徑的每一邊,如下中間圖所示。同樣將殘余圖更新如下右圖所示。
第三步:從上圖的殘余圖中我們已經可以看出來最后一步的唯一一種走法了。
做如下圖所示更新。
很顯然無法走到,因此算法至此便終止了。因此正好5個單位的流是最大值。
如果一開始我們選擇了如下圖中間所示的走法,那么算法就會失敗了,因為路已經被堵死了。
為了使算法得以成功運作,那么就要讓流圖中具有以相反方向發送流的路徑,如下所示。那么對于如下右圖中的殘余圖而言,從d返回到a的便成了3而非4,這是因為從t流到d的流量是3個單位。現在在殘余圖中就有a和d之間有2個方向,或者還有1個單位的流可以從導向,或者是3個單位的流導向相反的反向,當然,我們也可以撤銷流。
緊接著如果通過到導入2個單位的流,算法就會從邊取走2個單位的流,更新流圖如下。
參考:
1.《數據結構》、《離散數學》、《算法導論》、《挑戰程序設計競賽》等書籍和必應、維基百科等網絡。
2.https://blog.csdn.net/u012469987/article/details/51319574
3.https://www.cnblogs.com/hxsyl/p/3270401.html
4.https://wk.baidu.com/view/343fcfe8172ded630b1cb6f9
5.https://wk.baidu.com/view/be1b0a7f58cfa1c7aa00b52acfc789eb172d9e07
總結
以上是生活随笔為你收集整理的warshall算法求传递闭包c++_【建模小课堂】图论算法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: kafka 重新分配节点_Kafka扩容
- 下一篇: pillow python 划线_Pyt