寻找全局最小割(Stoer-Wagner 算法)
给定无向图,求全局最小割,Stoer-Wagner 算法过程如下:
方便起见,使用邻接矩阵存图,重边累加边权,记为 \(adj\).
维护活跃集合 \(act\),重复下面操作 \(n-1\) 轮:
维护一个集合 \(A\),定义
\[W_u = \sum_{v\in A}{adj_{u,v}}
\]
即 \(u\) 与集合 \(A\) 中所有点的边权累加和.
重复 \(|act|\) 轮,每轮寻找 \(W_u\) 最大值加入集合 \(A\),最终所有点均加入集合 \(A\).
记最后加入集合 \(A\) 的点为 \(t\),倒数第二加入集合 \(A\) 的点为 \(s\),不难发现,\(W_t\) 是割 \(\{t\} \mid V\setminus \{t\}\).
Stoer-Wagner 定理指出:此时 \(W_t\) 是 \(s,t\) 的最小割,因此可以做如下操作:取 \(W_t\) 作为全局最小割候选,合并 \(s,t\).
若 \(W_t\) 不是全局最小割,合并 \(s,t\) 不影响其他割,从而保证了正确性.
时间复杂度 \(\mathcal{O}(n^3)\).
//author:kzssCCC
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll INF = 9e18;
void solve(){
int n,m;
cin >> n >> m;
vector<vector<ll>> adj(n+1,vector<ll>(n+1));
for (int i=0;i<m;i++){
int u,v,w;
cin >> u >> v >> w;
adj[u][v] += w;
adj[v][u] += w;
}
vector<int> act(n);
iota(act.begin(),act.end(),1);
vector<ll> W(n+1);
vector<bool> vis(n+1);
ll minn = INF;
while (act.size()>1){
fill(W.begin(),W.end(),0);
fill(vis.begin(),vis.end(),false);
int len = act.size();
int pre = -1;
for (int t=1;t<=len;t++){
int cur = -1;
ll mx = -INF;
for (auto& v:act){
if (!vis[v] && W[v]>mx){
mx = W[v];
cur = v;
}
}
if (t==len){
minn = min(minn,W[cur]);
for (auto& v:act){
if (v==pre || v==cur) continue;
adj[pre][v] += adj[cur][v];
adj[v][pre] += adj[v][cur];
}
auto it = find(act.begin(),act.end(),cur);
act.erase(it);
}
else{
pre = cur;
vis[cur] = true;
for (auto& v:act){
if (!vis[v]){
W[v] += adj[cur][v];
}
}
}
}
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t = 1;
cin >> t;
while (t--) solve();
return 0;
}

浙公网安备 33010602011771号