【定義】
【最大流】
從源點向連邊流出流量 fi ,總計為 f,在到達匯點時,對每條邊的流量限制ei都有,fi<ci
令 f 盡量大,這個 f 被稱為最大流
【最小割】
有圖 V,給出點 s,t,去掉一條邊的代價為其流量限制,求使 s 無法到 t 的最小代價
這個代價被稱為最小割
經過一些我看不懂(我會繼續看的QAQ)證明,我們可以得到結論
【結論】 最大流等於最小割
【方法】
【Ford】
通過增加反向流量的方法,提供一種“反悔“的操作,從而支持流量從源點到匯點的改變和增加
新增加的路徑被稱為增廣路
記得當時學匈牙利算法時候看過

#include<cstdio> #include<iostream> #include<algorithm> #include<cstring> using namespace std; const int MAXN = 10010; const int MAXM = 200010; const int INF = (1 << 31) - 1; int n, m, s, t; struct V { int to; int rev; int cap; int nt; }edge[MAXM]; int st[MAXN], top=0; int vis[MAXN]; void add(int x, int y, int val) { top++; edge[top] = { y, top + 1, val, st[x] }; st[x] = top; top++; edge[top] = { x, top - 1, 0 , st[y] }; st[y] = top; } int dfs(int num, int srm) { if (num == t) return srm; vis[num] = 1; for (int i = st[num]; i!=-1; i = edge[i].nt) { int to = edge[i].to; if (!vis[to] && edge[i].cap > 0) { int minn = min(srm, edge[i].cap); int d = dfs(to, minn); if (d <= 0) continue; edge[i].cap -= d; edge[edge[i].rev].cap += d; return d; } } return 0; } int max_flow() { int flow = 0; while (1) { memset(vis, 0, sizeof(vis)); int d = dfs(s, INF); if (!d) break; flow += d; } return flow; } int main() { scanf("%d%d%d%d", &n, &m, &s, &t); memset(st, -1, sizeof(st)); for (int i = 1; i <= m; i++) { int x, y, z; scanf("%d%d%d", &x, &y, &z); add(x, y, z); } printf("%d", max_flow()); return 0; }
【dinic】
通過對節點進行分層的操作,從而使增廣路的效率提高(使其不會向回運動)
同時,對走過的路進行標記(或者取消),通過節省路徑數,提高效率

#include<cstdio> #include<queue> #include<cstring> #define ll long long using namespace std; const int MAXN = 10010; const int MAXM = 200010; const ll INF = (1ll << 62) - 1; struct note { int to; int nt; int rev; ll cal; }; struct edge { note arr[MAXM]; int st[MAXN]; int dis[MAXN]; int cur[MAXN]; int depth[MAXN]; int top; int n, m, s, t; edge() { memset(st, -1, sizeof(st)); memset(depth, -1, sizeof(depth)); memset(dis, -1, sizeof(dis)); top = 0; } void read() { top = 0; scanf("%d%d%d%d", &n, &m, &s, &t); for (int i = 1; i <= m; i++) { int x, y; ll z; scanf("%d%d%lld", &x, &y, &z); add(x, y, z); } } bool dep() { queue<int> q; q.push(s); memset(depth, -1, sizeof(depth)); depth[s] = 0; while (!q.empty()) { int v = q.front(); q.pop(); for (int i = st[v]; i != -1; i = arr[i].nt) { int to = arr[i].to; if (!arr[i].cal) continue; if (depth[to] != -1) continue; depth[to] = depth[v] + 1; q.push(to); } } return (depth[t] != -1); } void add(int x, int y, ll z) { top++; arr[top] = { y,st[x],top + 1,z }; st[x] = top; top++; arr[top] = { x,st[y],top - 1,0 }; st[y] = top; } ll min_dis() { } ll dfs(int now, ll val) { if (now == t || !val) return val; ll flow = 0; for (int& i = cur[now]; i != -1; i = arr[i].nt) { int to = arr[i].to; if (depth[to] != depth[now] + 1) continue; ll f = dfs(to, min(arr[i].cal, val)); if (!f || !arr[i].cal) continue; flow += f; arr[i].cal -= f; arr[arr[i].rev].cal += f; val -= f; if (!val) return flow; } return flow; } ll dinic() { ll flow = 0; ll f; while (dep()) { for (int i = 1; i <= n; i++) cur[i] = st[i]; while (f = dfs(s, INF)) flow += f; } return flow; } }; edge road; int main() { int T; road.read(); printf("%lld", road.dinic()); return 0; }
注意:上面板子中需要更改的東西
read():可以理解為建圖的函數
MAXN:點的個數
n,siz:這里需要區分出題目所給能夠給出信息的點和之后建聯通圖之后獲得的圖上點的個數
INF:最大值,如果改變元素:cal的數據類型,則數據可能會因為之前LL 的 INF太大然后爆掉
【擴展】
汪聚聚上課說圖論題大多數
要么是多種圖論知識的結合,(比如多校有道先求最短路徑然后在最短路徑上跑最小割的
要么是隱式圖,只要能夠成功建立出相應的圖,只需要跑個模板就可以了
而網絡流通常都是,板子幾乎不變,但是題的難度和建圖難度相同的那種
以下是幾種方法的舉例和講解:
2 . 拆流入流出,通過建圖和網絡流篩選不符合情況