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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

最大流问题(超详细!!!)

發布時間:2023/12/20 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 最大流问题(超详细!!!) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

0x00 最大流

最大流量問題涉及通過最大單源單匯流網絡找到可行流量。如圖所示。

每個邊緣都標有容量,即可以攜帶的最大物品數量。 最大流問題是找出可以從頂點source到頂點sink總的物品數量(最大的那個)。

如上圖所示,當0->1和2->1匯聚的時候不能超出1->3的值12(1的輸出最大為12)。最后可以到達5的最大物品個數就是19+4=23。也就是每條邊和每個頂點要滿足:

  • 每條邊上的流量不超過每條邊的給定容量。
  • 除source和sink之外,每個頂點的流入流等于流出流。

0x01 殘差網絡和增廣路徑

對于解決最大流的問題,我們首先將問題簡單化,如圖所示

首先可以想到的思路就是每次流過的值最大就好啦。所以,從S出發每次選擇可以流出的最大流。

通過這種方式的得到的最后結果是3,顯然這是錯誤的(應該是5)。我們首先將上面的結果中被刪除的部分拿出來(沒有被使用的流)。

通過該刪除部分可以構成殘差網絡。給定圖形GGG和其中的流fff,我們形成一個新的流網絡GfG_fGf?,該網絡具有相同的頂點集GGG,并且每個GGG邊緣具有兩個邊。對于圖GGG的邊e=(1,2)e =(1,2)e=(1,2) ,對應流為f(e)f(e)f(e),容量為c(e)c(e)c(e),形成的GfG_fGf?的方向為1->2的邊(前向弧)容量為c(e)?f(e)c(e)-f(e)c(e)?f(e),方向為2->1的邊(后向弧)的容量為f(e)f(e)f(e)。 殘差網絡如圖所示:

參差網絡的思想對于解決最大流問題非常有用。

還有一個需要用到的概念是增廣路徑。所謂增廣路徑,是指這條路徑上的流可以修改,通過修改,使得整個網絡的流值增大。 設fff是一個可行流,PPP是從源點sss到終點(匯點)ttt的一條路,若ppp滿足下列條件:

  • ppp上的所有前向弧(vi→vj)(v_i→v_j)(vi?vj?)都是非飽和弧,即0≤fi,j<ci,j0≤f_{i,j}<c_{i,j}0fi,j?<ci,j?
  • ppp上的所有后向弧(vi←vj)(v_i←v_j)(vi?vj?)都是非零弧,即0<fi,j≤ci,j0<f_{i,j}≤c_{i,j}0<fi,j?ci,j?

則稱ppp為(關于可行流fff的)一條可增廣路徑。

0x02 Ford-Fulkerson算法

首先需要知道最大流最小割定理。如果fff是具有源點sss和匯點ttt的流網絡G=(V,E)G = (V, E)G=(V,E)中的一個流,則下列條件是等價的:

  • fffGGG的一個最大流。
  • 殘留網絡GfG_fGf?不包含增廣路徑。
  • GGG的某個割(S,T)(S, T)(S,T),有∣f∣=c(S,T)|f| = c(S, T)f=c(S,T)。

上述定理的證明可以參看這里定理證明,這里就不細說。

Ford-Fulkerson算法是一種迭代方法。開始時,對所有u,v∈Vu, v ∈ Vu,vVf(u,v)=0f(u, v) = 0f(u,v)=0,即初始狀態時流的值為000。在每次迭代中,可通過尋找一條增廣路徑來增加流值。增廣路徑可以看做是從源點sss到匯點ttt之間的一條路徑,沿該路徑可以壓入更多的流,從而增加流的值。反復進行這一過程,直至增廣路徑都被找出為止。最大流最小割定理將說明在算法終止時,這一過程可產生出最大流。

from collections import defaultdict class Graph: def __init__(self,graph): self.graph = graph self.ROW = len(graph) def bfs(self, s, t, parent):visited = [False]*self.ROWqueue = [s] visited[s] = Truewhile queue: u = queue.pop(0) for ind, val in enumerate(self.graph[u]): if visited[ind] == False and val > 0 : queue.append(ind) visited[ind] = Trueparent[ind] = u return True if visited[t] else Falsedef FordFulkerson(self, source, sink): parent = [-1]*self.ROW max_flow = 0 while self.bfs(source, sink, parent): #判斷增廣路徑 path_flow, s = float("Inf"), sinkwhile s != source: #計算增廣路徑的最小流量,通過最小流量計算殘差網絡path_flow = min(path_flow, self.graph[parent[s]][s]) s = parent[s] max_flow += path_flow v = sink while v != source: #計算殘差網絡u = parent[v] self.graph[u][v] -= path_flow self.graph[v][u] += path_flow v = parent[v] return max_flow graph = [[0, 16, 13, 0, 0, 0], [0, 0, 10, 12, 0, 0], [0, 4, 0, 0, 14, 0], [0, 0, 9, 0, 0, 20], [0, 0, 0, 7, 0, 4], [0, 0, 0, 0, 0, 0]] g = Graph(graph) source, sink = 0, 5 print("The maximum possible flow is %d " % g.FordFulkerson(source, sink))

該算法的時間復雜度是O(VE2)O(VE^2)O(VE2)。

0x03 Dinic算法

Ford-Fulkerson算法對于稀疏圖來說還不錯,但是當邊的數目過多的時候我們就需要更好的Dinic算法,該算法的時間復雜度是O(EV2)O(EV^2)O(EV2)

首先使用給定的圖初始化殘差網絡(和之前做法一樣),如圖所示。

第一次迭代:我們使用BFS為所有節點分配級別,同事需要檢查是否有增廣路徑(殘差網絡中是否存在s→ts→tst路徑)。

現在,我們使用級別來發送流(意味著每個流路徑的級別都應為0、1、2、3)。與Ford-Fulkerson算法相比,我們一次發送三個流。

  • 路徑s–1–3–ts – 1 – 3 – ts13t上有4個流量單位。
  • 路徑s–1–4–ts – 1 – 4 – ts14t上有6個流量單位。
  • 路徑s–2–4–ts – 2 – 4 – ts24t上有4個流量單位。

總流量=4 + 6 + 4 = 14。一輪迭代后,殘差圖變為下圖。

第二次迭代:我們使用bfs遍歷上述修改后的殘差圖,為所有節點分配新級別。

現在,我們使用級別來發送流(意味著每個流路徑的級別都應為0、1、2、3、4)。 這次我們只能發送一個流。

  • 路徑s–2–4–3–ts – 2 – 4 – 3 – ts243t上有5個流量單位

總流量=總流量+ 5 = 19。二輪迭代后,殘差圖變為下圖。

第二次迭代:此時已經沒有增廣路徑了,所以結束。

#include <iostream> #include <cstring> using namespace std;const int N = 1e5 + 10, M = N * 2; struct Edge {int to, next, w; } edge[M];int cur[N], head[N], idx = -1; //cur用于當前弧優化 int n, m, s, t;void add(int u, int v, int w) {edge[++idx].to = v;edge[idx].next = head[u];edge[idx].w = w;head[u] = idx; }int q[N], level[N]; bool bfs(int u, int v) //判斷是否存在增廣路徑,構建路徑級別 {int hh = 0, tt = 0;q[0] = u;memset(level, -1, sizeof level);level[u] = 0;while (hh <= tt){int tmp = q[hh++];for (int i = head[tmp]; ~i; i = edge[i].next){int son = edge[i].to;if (edge[i].w <= 0 || ~level[son]) continue;level[son] = level[tmp] + 1;q[++tt] = son;}}return ~level[v]; }int dfs(int u, int v, int flow) //尋找增廣路徑 {if (u == v) return flow;int res = 0;//注意使用引用,這樣i變化的同時也能改變cur[u]的值,達到記錄當前弧的目的for (int& i = cur[u]; ~i; i = edge[i].next){int son = edge[i].to;if (edge[i].w <= 0 || level[son] != level[u] + 1) continue;if (res = dfs(son, v, min(edge[i].w, flow))){edge[i].w -= res; edge[i^1].w += res;return res;}}return res; }int dinic(int u, int v) {int res = 0;while (bfs(u, v)){for (int i = 0; i <= n; i++) cur[i] = head[i];res += dfs(u, v, 0x3f3f3f3f);}return res; }int main() {cin >> n >> m >> s >> t;memset(head, -1, sizeof head);for (int i = 0; i < m; ++i){int a, b, w;cin >> a >> b >> w;add(a, b, w), add(b, a, 0);}cout << dinic(s, t);return 0; }

reference:

https://www.geeksforgeeks.org/max-flow-problem-introduction/

https://www.geeksforgeeks.org/ford-fulkerson-algorithm-for-maximum-flow-problem/

https://www.geeksforgeeks.org/dinics-algorithm-maximum-flow/

https://www.cnblogs.com/gaochundong/p/ford_fulkerson_maximum_flow_algorithm.html

總結

以上是生活随笔為你收集整理的最大流问题(超详细!!!)的全部內容,希望文章能夠幫你解決所遇到的問題。

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