網路流(最大流)
給定一個網路,有源點和匯點,現在要往源點灌水,問每單位時間可以從匯點出多少水,並且每一條邊有限流。
P3376【模板】網路最大流
一個幾乎沒用的東西:FF
思路
我們很顯然會有個思路,就是每次 \(DFS\) 搜尋,找到一條路徑,並且是可以增廣的(限流還沒達到),那麼就增廣它,可是如果遇到一條路徑走會滿流,但分開走就能流出最大流的情況,所以我們需要一個反悔的機會,可以將剩餘可以流的水量建一條正向邊形成殘餘網路,已經流的建一條反向邊,於是就有了一個反悔的機會。
EK
思路
將 \(DFS\) 改成 \(BFS\) 即可,這樣可以保證每次找到的路徑是最短的,但是我們在增廣的時候需要把路徑弄出來,所以我們用一個 \(pre\) 陣列來記錄上一個結點,這樣就可以記錄路徑了。
code
#include <iostream>
#include <queue>
using namespace std;
using ll = long long;
const int MaxN = 210, MaxM = 2 * 5010;
struct Edge {
ll v, w, nxt;
} e[MaxM];
ll h[MaxN], ans;
int n, m, s, t, cnt = 1;
bool vis[MaxN];
queue<int> q;
pair<int, int> p[MaxN];
ll G(int x, ll minx = 1e18) {
if (x == s) {
return minx;
}
ll res = G(p[x].first, min(minx, e[p[x].second].w));
e[p[x].second ^ 1].w += res;
e[p[x].second].w -= res;
return res;
}
void Record(int u, int i) {
if (vis[e[i].v] || !e[i].w) {
return;
}
vis[e[i].v] = 1;
p[e[i].v] = {u, i};
q.push(e[i].v);
}
ll BFS() {
for (int i = 1; i <= n; i++) {
vis[i] = 0, p[i] = {0, 0};
}
queue<int>().swap(q);
for (q.push(s), vis[s] = 1; !q.empty(); q.pop()) {
int u = q.front();
if (u == t) {
return G(t);
}
for (int i = h[u]; ~i; i = e[i].nxt) {
Record(u, i);
}
}
return -1;
}
int main() {
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> m >> s >> t;
fill(h + 1, h + n + 1, -1);
for (ll i = 1, u, v, w; i <= m; i++) {
cin >> u >> v >> w;
e[++cnt] = {v, w, h[u]}, h[u] = cnt;
e[++cnt] = {u, 0, h[v]}, h[v] = cnt;
}
for (ll res; ~(res = BFS()); ans += res) {
}
cout << ans << '\n';
return 0;
}
時間複雜度:\(O(nm^2)\)
Dinic
每次對殘餘網路進行分層,將非可是分層的邊(樹上就是橫向邊)刪了,然後找當前圖的最大流,及找到增廣路徑,然後增廣,將當前圖的最大流併入最終的最大流。
最佳化
- 在 \(DFS\) 途中,每次將當前邊,設為當前點第一條邊(注意最後你要還原),因為當處理完這條邊後,再處理的話,必然會再一條被增廣的路徑上被卡死。
- 在 \(DFS\) 過程中,如果處理完一條邊的答案為 \(0\),那我們標記一下,返回過程中就不走這了,同樣的要還原。
code
#include <iostream>
#include <queue>
using namespace std;
using ll = long long;
const int MaxN = 210, MaxM = 2 * 5010;
struct IF {
struct Edge {
ll v, w, nxt;
} e[MaxM];
ll dis[MaxN], h[MaxN], th[MaxN], ans, n, s, t, cnt;
bool vis[MaxN];
queue<int> q;
IF() {
n = s = t = ans = 0, cnt = 1;
fill(h, h + MaxN, -1);
fill(dis, dis + MaxN, 0);
}
void Record(int u, int i) {
if (vis[e[i].v] || !e[i].w) {
return;
}
vis[e[i].v] = 1;
dis[e[i].v] = dis[u] + 1;
q.push(e[i].v);
}
bool BFS() {
for (int i = 1; i <= n; i++) {
vis[i] = 0, th[i] = h[i];
}
queue<int>().swap(q);
for (q.push(s), vis[s] = 1, dis[s] = 1; !q.empty(); q.pop()) {
int u = q.front();
if (u == t) {
continue;
}
for (int i = h[u]; ~i; i = e[i].nxt) {
Record(u, i);
}
}
return vis[t];
}
ll DFS(int x, ll f) {
if (x == t || !f) {
return f;
}
ll res = 0;
for (int i = th[x]; ~i && f - res > 0; i = e[i].nxt) {
th[x] = i;
if (e[i].w && dis[x] + 1 == dis[e[i].v]) {
ll tmp = DFS(e[i].v, min(f - res, e[i].w));
if (!tmp) {
dis[e[i].v] = 1e18;
}
res += tmp;
e[i].w -= tmp;
e[i ^ 1].w += tmp;
}
}
return res;
}
void insert(int u, int v, ll w) {
e[++cnt] = {v, w, h[u]}, h[u] = cnt;
e[++cnt] = {u, 0, h[v]}, h[v] = cnt;
}
ll Solov() {
for (; BFS(); ans += DFS(s, 1e18)) {
}
return ans;
}
};
int n, m, s, t;
int main() {
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> m >> s >> t;
IF ans;
ans.n = n, ans.s = s, ans.t = t;
for (ll i = 1, u, v, w; i <= m; i++) {
cin >> u >> v >> w;
ans.insert(u, v, w);
}
cout << ans.Solov() << '\n';
return 0;
}
時間複雜度
最差:\(O(n^2m)\) 若為二分圖,可以到 \(O(m\sqrt n)\)