Nubulsa Expo (全局最小割)

一、前言

全局最小割板题。

二、题目

Vjudge

求给定源点的无向图最小割。

三、讲解

(一)、科技:全局最小割

首先我们有个连我都能感性理解的算法:任选两个点,求出最小割并更新答案,合并这两个点,最后求出的答案即为全局最小割。这其实就是并不出名大名鼎鼎的 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;
}
posted @ 2022-02-22 10:11  皮皮刘  阅读(46)  评论(2编辑  收藏  举报