Stoer-Wagner 算法
概念¶
由于取消了 源汇点 的定义,我们需要对 割 的概念进行重定义。
(其实是网络流部分有关割的定义与维基百科不符,只是由于一般接触到的割都是「有源汇的最小割问题」,因此这个概念也就约定俗成了。)
割¶
去掉其中所有边能使一张网络流图不再连通(即分成两个子图)的边集称为图的割。
即:在无向图
有源汇点的最小割问题¶
同 最小割 中的定义。
无源汇点的最小割问题¶
包含的弧的权和最小的割。也称为全局最小割。
显然,直接跑网络流的复杂度是行不通的。
Stoer-Wagner 算法¶
Stoer-Wagner 算法在 1995 年由Mechthild Stoer与Frank Wagner提出,是一种通过 递归 的方式来解决 无向正权图 上的全局最小割问题的算法。算法复杂度
它的实现基于以下基本事实:设图
算法流程¶
- 在图
G s, t G S-T - 「合并」点
s, t G |V| 1 - 输出所有cut of phase的最小值。
合并两点
解释:如果
步骤 1 考虑了
S-T 最小割的求法¶
(显然不是网络流。)
假设进行若干次合并以后,当前图
我们构造一个集合
我们每次将
其中权值函数的定义:
(若
容易知道所有点加入
则对任意点
证明算法正确性¶
定义一个点
定义
定义诱导割
Lemma 1
对于任何被激活的点
证明:使用数学归纳法。
对于第一个被激活的点
对于之后两个被激活的点
又,已知:
由于
由归纳法得证。
由于
P5632 【模板】Stoer-Wagner算法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | #include <bits/stdc++.h>
using namespace std;
const int N = 601;
int fa[N], siz[N], edge[N][N];
int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
int dist[N], vis[N], bin[N];
int n, m;
int contract(int &s, int &t) { // Find s,t
memset(dist, 0, sizeof(dist));
memset(vis, false, sizeof(vis));
int i, j, k, mincut, maxc;
for (i = 1; i <= n; i++) {
k = -1;
maxc = -1;
for (j = 1; j <= n; j++)
if (!bin[j] && !vis[j] && dist[j] > maxc) {
k = j;
maxc = dist[j];
}
if (k == -1) return mincut;
s = t;
t = k;
mincut = maxc;
vis[k] = true;
for (j = 1; j <= n; j++)
if (!bin[j] && !vis[j]) dist[j] += edge[k][j];
}
return mincut;
}
const int inf = 0x3f3f3f3f;
int Stoer_Wagner() {
int mincut, i, j, s, t, ans;
for (mincut = inf, i = 1; i < n; i++) {
ans = contract(s, t);
bin[t] = true;
if (mincut > ans) mincut = ans;
if (mincut == 0) return 0;
for (j = 1; j <= n; j++)
if (!bin[j]) edge[s][j] = (edge[j][s] += edge[j][t]);
}
return mincut;
}
int main() {
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> m;
if (m < n - 1) {
cout << 0;
return 0;
}
for (int i = 1; i <= n; ++i) fa[i] = i, siz[i] = 1;
for (int i = 1, u, v, w; i <= m; ++i) {
cin >> u >> v >> w;
int fu = find(u), fv = find(v);
if (fu != fv) {
if (siz[fu] > siz[fv]) swap(fu, fv);
fa[fu] = fv, siz[fv] += siz[fu];
}
edge[u][v] += w, edge[v][u] += w;
}
int fr = find(1);
if (siz[fr] != n) {
cout << 0;
return 0;
}
cout << Stoer_Wagner();
return 0;
}
|
复杂度分析与优化¶
contract操作的复杂度为
一共进行
根据 最短路 的经验,算法瓶颈在于找到权值最大的点。
在一次contract中需要找
斐波那契堆 可以胜任
(实际测试中开 O2 还要卡评测波动才能过。)
build本页面最近更新:,更新历史
edit发现错误?想一起完善? 在 GitHub 上编辑此页!
people本页面贡献者:DanJoshua, opsiff
copyright本页面的全部内容在 CC BY-SA 4.0 和 SATA 协议之条款下提供,附加条款亦可能应用