ISAP演算法
ISAP 的意思是 Improved SAP ,即改進的 SAP ,而 SAP(Shortest Augument Path,最短增廣路)演算法其實指的就是 Edmonds-Karp 演算法。但事實上,ISAP 更像是 Dinic ,而不是 SAP。
先貼非遞迴版程式碼,以後再寫原理。
//
// Created by Visors on 2020/9/27.
//
// 題目名:【模板】網路最大流
// 題目來源:luogu
// 題目連結:https://www.luogu.com.cn/problem/P3376
// 演算法:ISAP.cpp
// 用途:TODO
// 時間複雜度:O(TODO)
//
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int oo = 0x7fffffff;
struct Edge {
int from; // 起點
int to; // 終點
int capacity; // 容量
int flow; // 流量
int next; // 下條邊
Edge() = default;
Edge(int from, int to, int capacity, int flow, int next) : from(from), to(to), capacity(capacity), flow(flow), next(next) {}
};
vector<Edge> edges; // 邊集
vector<int> heads; // 首邊集
vector<int> levels; // 層級
vector<bool> vis; // 是否增廣
vector<int> cur; // 當前弧
vector<int> num; // 每層節點數,用於GAP優化
vector<int> paths; // 增廣路
int n, m, s, t;
inline void addEdge(int u, int v, int c, int f) {
edges.emplace_back(v, u, 0, -f, heads[v]);
heads[v] = edges.size() - 1;
edges.emplace_back(u, v, c, f, heads[u]);
heads[u] = edges.size() - 1;
}
bool bfs() {
fill(vis.begin(), vis.end(), false);
queue<int> q;
q.push(t); // 從匯點開始分層
vis[t] = true;
levels[t] = 0;
while (!q.empty()) {
int v = q.front();
q.pop();
for (int i = heads[v]; ~i; i = edges[i].next) {
// 方向相反,此時反向邊才是入邊
Edge &e = edges[i ^ 1];
int &u = e.from;
if (!vis[u] && e.capacity > e.flow) {
vis[u] = true;
levels[u] = levels[v] + 1;
q.push(u);
}
}
}
return vis[s];
}
ll augment() {
int u = t;
ll imp = oo;
while (u != s) {
Edge &e = edges[paths[u]];
imp = min(imp, (ll)e.capacity - e.flow);
u = e.from;
}
u = t;
while (u != s) {
edges[paths[u]].flow += imp;
edges[paths[u] ^ 1].flow -= imp;
u = edges[paths[u]].from;
}
return imp;
}
ll maxFlow() {
ll ret = 0;
bfs(); // ISAP在DFS中動態修改分層levels一遍BFS預處理即可
fill(num.begin(), num.end(), 0);
for (int it:levels) num[it]++; // 統計每層節點數
int u = s;
for (int i = 0; i < n; i++) cur[i] = heads[i]; // 當前弧初始化
// 下面其實在模擬DFS
while (levels[s] < n) {
// 如果找到匯點,開始增廣
if (u == t) {
ret += augment();
u = s;
}
bool ok = false;
// 找一條可增廣邊
for (int &i = cur[u]; ~i; i = edges[i].next) {
Edge &e = edges[i];
int &v = e.to;
if (levels[u] == levels[v] + 1 && e.capacity > e.flow) {
ok = true;
paths[v] = i;
u = v;
break;
}
}
// 如果沒找到,說明正向邊殘量全為0,反向邊殘量>0,更新層數
if (!ok) {
int minLevel = n - 1;
for (int i = heads[u]; ~i; i = edges[i].next) {
Edge &e = edges[i];
int &v = e.to;
// 更新為最小層數+1
if (e.capacity > e.flow) minLevel = min(minLevel, levels[v]);
}
if (--num[levels[u]] == 0) break; // GAP優化
num[levels[u] = minLevel + 1]++; // 層數變化
cur[u] = heads[u]; // 當前弧重置
if (u != s) u = edges[paths[u]].from; // 向前回溯一個節點
}
}
return ret;
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
scanf("%d%d%d%d", &n, &m, &s, &t);
s--, t--;
edges.clear();
heads.resize(n, -1);
levels.resize(n);
vis.resize(n);
cur.resize(n);
num.resize(n);
paths.resize(n);
for (int i = 1, u, v, c; i <= m; i++) {
scanf("%d%d%d", &u, &v, &c);
addEdge(u - 1, v - 1, c, 0);
}
cout << maxFlow() << endl;
return 0;
}
相關文章
- Dinic/ISAP求最大流
- 演算法-回溯演算法演算法
- 【演算法】KMP演算法演算法KMP
- 【JAVA演算法】圖論演算法 -- Dijkstra演算法Java演算法圖論
- 演算法(2)KMP演算法演算法KMP
- 【演算法】遞迴演算法演算法遞迴
- 演算法題:洗牌演算法演算法
- 介面限流演算法:漏桶演算法&令牌桶演算法演算法
- 演算法初探--遞迴演算法演算法遞迴
- 分類演算法-AdaBoot 演算法演算法boot
- 前端演算法:快速排序演算法前端演算法排序
- 常用演算法 插值演算法演算法
- BP演算法和LMBP演算法演算法
- c/c++ 通用的(泛型)演算法 之 只讀演算法,寫演算法,排序演算法C++泛型演算法排序
- 演算法金 | 突破最強演算法模型,決策樹演算法!!演算法模型
- 介面限流演算法:漏桶演算法&令牌桶演算法&redis限流演算法Redis
- 常用演算法之貪心演算法演算法
- 演算法(八):圖解KNN演算法演算法圖解KNN
- 複習常用演算法_冒泡演算法演算法
- 演算法修養--A*尋路演算法演算法
- 演算法那些事之冒泡演算法演算法
- 基礎演算法之排序演算法演算法排序
- 最短路演算法之:Dijkstra 演算法演算法
- 最短路演算法之:floyd 演算法演算法
- 【JAVA演算法】排序演算法 -- 快速排序Java演算法排序
- 演算法進階(8): EM演算法演算法
- 最短路-SPFA演算法&Floyd演算法演算法
- 【JAVA演算法】圖論演算法 --求最小生成樹Prim演算法Java演算法圖論
- 演算法演算法
- 演算法設計與分析中的幾個核心演算法策略:動態規劃、貪心演算法、回溯演算法和分治演算法演算法動態規劃
- 演算法(六):圖解貪婪演算法演算法圖解
- 演算法學習 - 基礎排序演算法演算法排序
- 分類演算法-k 鄰近演算法演算法
- 【演算法備忘錄】-(1)-LRU演算法演算法
- KMP演算法和bfprt演算法總結KMP演算法
- 非對稱加密演算法-RSA演算法加密演算法
- 推薦演算法(二)--演算法總結演算法
- 尋路演算法之A*演算法詳解演算法