Stoer-Wagner算法,求解无向图的最小割

本文探讨了一种通过最小割思想解决网络优化问题的方法,通过逐个分析节点并合并最大边权的点,形成排列,利用定理确定每个活跃节点前的最小割。关键步骤包括证明每个活跃节点贡献的最小割价值,并在解决过程中迭代应用。算法核心在于找到最优的连通块划分,降低整体割集成本。

正题

Portal
这种思想值得学习!
我们知道最小割会将集合分为两部分S集和T集。
考虑随意选出两个点 A , B A,B A,B,显然在一个最小割中这两个点要么在同一个集合,要么不在同一个集合。
在同一个集合十分好做:我们只需要将这两个点合并起来看作一个点考虑就可以了(边加权合并),因为可以简单分析得到,对于任意的一个点 C C C,要么与 A , B A,B A,B在同一个集合,要么不在同一个集合。
如果我们知道不在同一个集合怎么做,我们就可以每次选两个点出来求一遍答案,然后合并(即考虑在同一个集合的答案),然后求所有这样答案的最小值即可,一共合并 n − 1 n-1 n1次。
做法依赖于一个集合 V V V,我们将当前与 V V V中的直接连边权值最大的点加入 V V V中(第一个无所谓),形成了一个排列,有定理:排列的最后一个点与前面点的最小割为最后一个点加入V时的权值。
我们先规定一些记号,在作证明:
1.记 w ( V , x ) w(V,x) w(V,x)表示集合 V V V中与 x x x的直接连边权值和。
2.记 V x V_x Vx表示 x x x前的点,不包括 x x x
2.记 W ( x ) W(x) W(x)表示 x x x与排列在其前面的直接连边权值和,也就是x加入排列时的权值。
3.记 C C C为排列最后一个元素的最小割边集。
4.若点 x x x满足排列中的前一个点 y y y,与 x x x不在同一个集合则称 x x x a c t i v e active active的,显然可以发现割 C C C将序列分为了很多段,每一段的开始都是一个 a c t i v e active active节点。
我们先尝试证明一个引理:对于第一个 a c t i v e active active的点 x x x,满足 C C C在其前面的部分 ≥ W ( x ) \geq W(x) W(x)
“在其前面的部分”指的是排列的前面这一段的诱导子图产生的诱导最小割。
因为 x x x是第一个 a c t i v e active active的点,所以前面的点都不是 a c t i v e active active的,也就是说,他们都处在同一个连通块内,而 x x x与他们处在不同的连通块内,所以至少要割去 W ( x ) W(x) W(x)
定理:对于所有的 a c t i v e active active的点 x x x,满足 C C C在其前面的部分 ≥ W ( x ) \geq W(x) W(x)
在上面的引理已经证明了这个对第一个点肯定满足。
设当前要考虑的 a c t i v e active active点为 x x x,上一个 a c t i v e active active点为 y y y
可以看到:对于在 x x x前面的点来说,到 x x x的权值和肯定是 ≥ \geq 到y的,因为 x x x y y y先加入。
也就是: w ( C x ) ≥ w ( V x , x ) ≥ w ( V x , y ) w(C_x)\geq w(V_x,x) \geq w(V_x,y) w(Cx)w(Vx,x)w(Vx,y)
其次, x x x y y y前这一段都与 y y y处于异侧,那么割 C C C肯定新包含这些边。
也就是: w ( V y − V x , y ) = w ( C y ) − w ( C x ) w(V_y-V_x,y)=w(C_y)-w(C_x) w(VyVx,y)=w(Cy)w(Cx)
可以得到: w ( V y , y ) = w ( V y − V x , y ) + w ( V x , y ) ≤ w ( C y ) w(V_y,y)=w(V_y-V_x,y)+w(V_x,y)\leq w(C_y) w(Vy,y)=w(VyVx,y)+w(Vx,y)w(Cy)
对于每一个 a c t i v e active active都满足这样的性质,而最后一个点一定是一个 a c t i v e active active点,因为在证明 C C C s − t s-t st割,我们只需要将最后两个点设为 s , t s,t s,t即可。满足条件 w ( C ) ≥ w ( V t , t ) w(C)\geq w(V_{t},t) w(C)w(Vt,t),而割去t与周围点的连边肯定可以将t与周围点隔开,所以 w ( C ) = w ( V t , t ) w(C)=w(V_{t},t) w(C)=w(Vt,t)

#include<bits/stdc++.h>
using namespace std;

const int N=610;
int n,m,d[N][N],w[N],a[N];
bool tf[N],vis[N];

int gs(int x){
	
	memset(vis,false,sizeof(vis));
	memset(w,0,sizeof(w));w[0]=-1;
	for(int i=1;i<=x;i++){
		int mx=0;
		for(int j=1;j<=n;j++)
			if(!tf[j] && !vis[j] && w[mx]<w[j]) mx=j;
		a[i]=mx;vis[mx]=true;
		for(int j=1;j<=n;j++)
			if(!tf[j] && !vis[j]) w[j]+=d[mx][j];
	}
	tf[a[x]]=true;
	for(int i=1;i<=n;i++){
		d[a[x-1]][i]+=d[a[x]][i];
		d[i][a[x-1]]+=d[i][a[x]];
	}
	return w[a[x]];
}

int solve(){
	int ans=1e9;
	for(int i=n;i>=2;i--) ans=min(ans,gs(i));
	return ans;
}

int main(){
	scanf("%d %d",&n,&m);
	int x,y,c;
	for(int i=1;i<=m;i++) scanf("%d %d %d",&x,&y,&c),d[x][y]+=c,d[y][x]+=c;
	printf("%d\n",solve());
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值