一、前言
全局最小割板题。
二、题目
求给定源点的无向图最小割。
三、讲解
(一)、科技:全局最小割
首先我们有个连我都能感性理解的算法:任选两个点,求出最小割并更新答案,合并这两个点,最后求出的答案即为全局最小割。这其实就是并不出名大名鼎鼎的 Stoer-Wagner算法。
由于是任选两个点求最小割,所以我们其实有比跑网络流更快的做法:
我们搞一个点集 \(A\),初始里面随便放个点,每次选择 \(dis(A,u)=\sum_{a\in A}dis(a,u)\) 最大的点加入点集。
设第 \(i\) 次加入的点是 \(u_i\),那么 \(cut_{u_{n-1},u_n}\) 割开的点集为 \(\{u_1,u_2,...,u_{n-1}\},\{u_n\}\)。
所以做一次上述做法就可以得到随机两个点的最小割,做 \(n-1\) 次就可以得到全局最小割。
这个过程可以用类似 Prim 或者说无堆优化的 dijkstra 的方式实现,时间复杂度是 \(O(n^3)\) 的,如果用堆优化,复杂度是 \(O(nm\log_2n)\) 的。
证明啥的可以去看集训队论文。
(二)、正题
之所以叫正题是因为这篇博客本应是题解。
我们可以注意到这个源点其实没啥用,我们要求的就是全局最小割,证明的话你就考虑全局最小割会把源点 \(S\) 割到一边,在另一边随便选个点其实就是题目所求给定源点的最小割。
知道这个就可以直接上板子了。
我用的是 \(O(n^3)\) 的做法。
四、代码
//12252024832524
#include <bits/stdc++.h>
#define TT template<typename T>
using namespace std;
typedef long long LL;
const int MAXN = 305;
const int MAXM = 50005;
const LL INF = 1ll << 60;
int n,m;
LL Read()
{
LL x = 0,f = 1;char c = getchar();
while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
return x * f;
}
TT void Put1(T x)
{
if(x > 9) Put1(x/10);
putchar(x%10^48);
}
TT void Put(T x,char c = -1)
{
if(x < 0) putchar('-'),x = -x;
Put1(x); if(c >= 0) putchar(c);
}
TT T Max(T x,T y){return x > y ? x : y;}
TT T Min(T x,T y){return x < y ? x : y;}
TT T Abs(T x){return x < 0 ? -x : x;}
bool vis[MAXN],vis2[MAXN];
LL e[MAXN][MAXN],dis[MAXN];
void Add_Double_Edge(int u,int v,int w){
e[u][v] += w;
e[v][u] += w;
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
while(1){
LL ans = INF;
n = Read(); m = Read(); Read();
if(!n && !m) return 0;
for(int i = 1;i <= n;++ i) vis[i] = 0;
for(int i = 1;i <= n;++ i) for(int j = 1;j <= n;++ j) e[i][j] = 0;
for(int i = 1,u,v;i <= m;++ i) u = Read(),v = Read(),Add_Double_Edge(u,v,Read());
for(int cas = 1;cas < n;++ cas){//contract
int now = 0,lst = 0,cnt = 0;
for(int i = 1;i <= n;++ i){//begin
if(!vis[i]) now = i,++cnt;
dis[i] = 0;
}
for(int i = 1;i <= n;++ i) if(!vis[i]) dis[i] = e[now][i],vis2[i] = 0;
vis2[now] = 1;
for(int i = 1;i < cnt;++ i){
lst = now; dis[now] = -1;
for(int j = 1;j <= n;++ j) if(!vis2[j] && dis[j] > dis[now]) now = j;
vis2[now] = 1;
for(int j = 1;j <= n;++ j) if(!vis2[j]) dis[j] += e[now][j];
}
ans = Min(ans,dis[now]); vis[now] = 1;
for(int i = 1;i <= n;++ i) e[lst][i] += e[now][i],e[i][lst] += e[i][now];
}
Put(ans,'\n');
}
return 0;
}