圖片內容並不是原創,來自巨神學長node
設G(V,E)爲一個有向圖,它的每條邊<u,v>都被賦予了一個非負的實數c做爲邊的容量,記爲c(u,v)。網絡流
(network flow)指爲這個有向圖分配流而且使得它每條邊上的流量都不能超過這條邊的容量。
在運籌學中,有向圖稱爲網絡,邊稱爲弧(arc)。在這個有向圖中指定兩個頂點分別叫作源點Vs和匯點Vt。
除源點和匯點,要保持每一個頂點進出總流量相同,源點的總出流量和匯點的總入流量相等。ios
初始化網絡及其網絡流算法
構造剩餘網絡和層次網絡,若匯點不在層次網絡中則算法結束。網絡
在層次圖G_L內用一次DFS過程進行增廣每次都向層次增長1的方向增廣,每次增廣完畢,在層次網絡中要去掉
因改進流量而致使飽和的弧,DFS執行完畢則該階段增廣完畢。spa
轉步驟2。.net
實際複雜度\(O(V^2E)\)3d
小貼士:在實際運行中遠遠達不到這個上界,通常能夠護理 到 規模的算法.其中求解二分圖時有更好的複雜度\(O(m\sqrt n )\)且實際上的複雜度比這個更加優秀code
首先設maxflow=0
如圖,源點爲0,匯點爲5,首先對其創建層次圖blog
每一個頂點右下方的數字就是其對應的層次。接下來進行DFS,找到一條流量爲4的增廣路。圖片
此時maxflow=4
回退至頂點3,發現它沒有其餘的容許弧,繼續回退至1。在回退的過程當中不斷修改流經的弧的容量。接下來,發現頂點1有一個容許弧連至4,繼續DFS。此時流量上限已經更新爲10-4=6(指<0,1>邊)。
又找到一條流量爲min(6,8,10)=6的增廣路。
此時的最大流量maxflow=4+6=10
此時頂點1的流量上限已經爲0,從頂點1回退至源點。修改相關弧的剩餘容量。
繼續搜索源點的相鄰頂點2,並找到一條增廣路。因爲源點的容量上限始終是INF,因此增廣路的流量爲min(10,9,4)=4。
此時的最大流量爲maxflow=4+6+4
此時節點二已經跑滿,回退到源點,並調整相應的弧的容量,發現源點沒有其餘的相鄰頂點,dfs過程結束
再次BFS,從新創建層次圖
找到一條增廣路,流量爲min(6,5,6,6)=5
此時的最大流量maxflow=4+6+4+5
發現沒有其餘增廣路,回退至源點,更新相關弧的容量。
源點沒有其餘的相鄰點,從新BFS創建新的層次網絡,發現匯點不在層次網絡中,算法結束
https://vjudge.net/problem/HDU-1532
最大流裸題,題意是有n個溝渠,可以流過必定量的水,有m個池塘,1爲源點,m爲匯點,求最大流
使用vector建圖在網絡流裏十分不方便,找到反向邊比較麻煩,在以後的最小費用最大流中更是十分麻煩,因此採用鏈式前向星建圖
#include <cstdio> #include <vector> #include <algorithm> #include <iostream> #include <queue> #include <cstring> #define N 250 using namespace std; const int inf = 0x3f3f3f3f; int m, n; struct node { int u, v, cap, nxt; node () {} node (int u, int v, int cap, int nxt): u(v), v(v), cap(cap), nxt(nxt) {} } edge[N * 10]; int head[N], tot; void init() { memset(head, -1, sizeof(head)); tot = 0; } int dep[N]; void adde(int u, int v, int w) { edge[tot] = node(u, v, w, head[u]); head[u] = tot++; edge[tot] = node(v, u, 0, head[v]); head[v] = tot++; } bool bfs(int s, int t) { queue<int> q; memset(dep, -1, sizeof(dep)); dep[s] = 0; q.push(s); while (!q.empty()) { int u = q.front(); q.pop(); for (int i = head[u]; ~i; i = edge[i].nxt) { int v = edge[i].v; if (edge[i].cap > 0 && dep[v] == -1) { dep[v] = dep[u] + 1; q.push(v); } } } return dep[t] > 0; } int dfs(int u, int t, int f) {//t爲終點 if (u == t) return f; for (int i = head[u]; ~i; i = edge[i].nxt) { int v = edge[i].v; int cap = edge[i].cap; if (cap > 0 && dep[u] < dep[v]) { int d = dfs(v, t, min(f, cap)); if (d) { edge[i].cap -= d; edge[i ^ 1].cap += d; return d; } } } return 0; } int dinic(int s, int t) { int maxflow = 0; while (bfs(s, t)) { int f; while ((f = dfs(s, t, inf)) > 0) maxflow += f; } return maxflow; } int main() { while (~scanf("%d%d", &m, &n)) { init(); for (int i = 1; i <= m; i++) { int u, v, w; scanf("%d%d%d", &u, &v, &w); adde(u, v, w); } printf("%d\n", dinic(1, n)); } return 0; }
最大流即最小割
在一個網絡中,可能存在多個總流量相同的最大流f,咱們能夠在計算流量的基礎之上,給網絡中的弧增長一個單位流量的費用(簡稱費用),在確保流量最大的前提下總費用最小,這樣的最大流被稱爲最小費用最大流。
在下面這個網絡中,弧上有逗號分隔的兩個數分別爲弧的容量和單位流量費用。例如,一條流量爲二、通過S->1->4->T的流的費用爲(3+5+4)*2=24。
網絡初始流量爲 0
在當前的剩餘網絡上求出從 S 到 T 的最短增廣路 min_dist(s,t) ,以每條弧的單位流量費用爲邊權,「距離」即爲該路徑上的邊的單位費用之和。若是不存在,則算法結束
找出這條路徑上的邊的容量的最小值 f ,則當前最大流 max_flow 擴充 f ,同時當前最小費用 min_cost 擴充 f * min_dist(s,t) 。
修改增廣路上弧的流量(正向邊的容量減小 f ,反向邊的容量增長 f ),重複步驟 2
\(O(尋找最短路 \times maxflow)\)
https://vjudge.net/problem/HDU-6118
最小費用最大流,首先創建超級源點 s ,與超級匯點 t 。
由於生產一個商品須要花費 a[i] 元,且上限爲 b[i] ,因此咱們從 s 向這些點之間連一條容量爲 b[i] ,費用爲 -a[i] 的邊。
一樣的道理,出售一個商品能夠賺到 c[i] 元,最多出售 d[i] 個,因而咱們從這些點向 t 連一條容量爲 d[i] ,費用爲 c[i] 的邊。
最後全部的公路也是花費,從 u 到 v 鏈接一條雙向邊,容量爲 INF ,費用爲 -k
這樣使用SPFA找最長路(即獲利最大)而後乘以這條路上的最大流便可
注意:圖中存在自環,當咱們獲得兩點路徑長度小於 0 時應終止計算。
#include <cstdio> #include <vector> #include <algorithm> #include <iostream> #include <queue> #include <cstring> #define N 550 using namespace std; const int inf = 0x3f3f3f3f; int n, m; struct node { int u, v, cap, cost, nxt; node() {} node (int u, int v, int cap, int cost, int nxt): u(u), v(v), cap(cap), cost(cost), nxt(nxt) {} } edge[10010]; int head[N], tot; void init() { memset(head, -1, sizeof(head)); tot = 0; } void adde(int u, int v, int w, int c) { edge[tot] = node(u, v, w, c, head[u]); head[u] = tot++; edge[tot] = node(v, u, 0, -c, head[v]);//迴流費用減小 head[v] = tot++; } int d[N], pre[N]; bool vis[N]; int spfa(int s, int t) { queue<int> q; memset(d, -inf, sizeof(d)); memset(pre, -1, sizeof(pre)); memset(vis, 0, sizeof(vis)); d[s] = 0; vis[s] = 1; q.push(s); while (!q.empty()) { int u = q.front(); q.pop(); vis[u] = false; for (int i = head[u]; ~i; i = edge[i].nxt) { int v = edge[i].v; if (edge[i].cap > 0 && d[v] < d[u] + edge[i].cost) { d[v] = d[u] + edge[i].cost; pre[v] = i; if (!vis[v]) { vis[v] = true; q.push(v); } } } } return d[t] != inf; } int cost = 0; void mincostmaxflow(int s, int t) { while (spfa(s, t)) { int minn = inf; for (int i = pre[t]; i != -1; i = pre[edge[i ^ 1].v]) { minn = min(minn, edge[i].cap); } for (int i = pre[t]; i != -1; i = pre[edge[i ^ 1].v]) { edge[i].cap -= minn; edge[i ^ 1].cap += minn; } if (d[t] < 0) break; cost += minn * d[t]; } } int main() { while (~scanf("%d%d", &n, &m)) { init(); int s = 0, t = n + 1; cost = 0; for (int i = 1; i <= n; i++) { int a, b, c, d; scanf("%d%d%d%d", &a, &b, &c, &d); adde(s, i, b, -a); adde(i, t, d, c); } for (int i = 1; i <= m; i++) { int u, v, c; scanf("%d%d%d", &u, &v, &c); adde(u, v, inf, -c); adde(v, u, inf, -c); } mincostmaxflow(s, t); cout << cost << endl; } return 0; }