bzoj3080 Minimum Variance Spanning Tree 最小方差生成树

本文介绍了一种基于枚举生成树边权和的优化算法,通过将边权修改为与平均值的差的平方,再进行最小生成树计算,以找到最优化解。文章详细解释了算法原理,包括如何确保枚举过程中的最优解,并提供了实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目分析

我们发现选出的生成树的边权和最多也只有2500,我们枚举选出的生成树边权和,于是就知道了平均数wˉ\bar{w}wˉ。然后将每条边的权值改为(wi−wˉ)2(w_i-\bar{w})^2(wiwˉ)2,做最小生成树。

这时候你可能会发问,为什么这样是对的,万一你在枚举了某个边权和后,出现了两种方案,一种边权和和我枚举的相同但是不优,另一种边权和不同但是更优,这样我们的做法就错了啊。

我们设想一下我们有一个向量a(w1,w2,...wn−1)(w_1,w_2,...w_{n-1})(w1,w2,...wn1)和一个向量b(y,y,...y)(y,y,...y)(y,y,...y),我们要最小化∑i=1n−1(wi−y)2\sum_{i=1}^{n-1} (w_i-y)^2i=1n1(wiy)2,也就是要最小化|a-b|,画画图可以知道,当ab垂直的时候最优。

而又因为b=y(1,1,...1)y(1,1,...1)y(1,1,...1),我们求一下a(1,1,...1)(1,1,...1)(1,1,...1)上的投影,即可算出最优的yyy,这个用点积搞一下,得到投影的长度yn=∑i=1n−1winy\sqrt{n}=\frac{\sum_{i=1}^{n-1} w_i}{\sqrt{n}}yn=ni=1n1wi,即当y=∑i=1n−1winy=\frac{\sum_{i=1}^{n-1} w_i}{n}y=ni=1n1wi,也就是平均值的时候,是最优的。

所以如果我们枚举的边权和与我们求出最小生成树中那些边的边权和不一样,一定不优,不会影响解。

代码

#include<bits/stdc++.h>
using namespace std;
#define RI register int
typedef double db;
const int M=1005;
db ans;int n,m,kas,f[55];
struct edge{int x,y;db w;}e[M],ke[M];
int find(int x) {return x==f[x]?x:f[x]=find(f[x]);}
int cmp(edge a,edge b) {return a.w<b.w;}
db getans(db sum) {
	sum/=(db)(n-1);db re=0;
	for(RI i=1;i<=m;++i)
		ke[i]=e[i],ke[i].w=(sum-e[i].w)*(sum-e[i].w);
	for(RI i=1;i<=n;++i) f[i]=i;
	sort(ke+1,ke+1+m,cmp);
	for(RI i=1;i<=m;++i) {
		int r1=find(ke[i].x),r2=find(ke[i].y);
		if(r1!=r2) re+=ke[i].w,f[r1]=r2;
	}
	return re;
}
int main()
{
   	while(~scanf("%d%d",&n,&m)&&n&&m) {
   		for(RI i=1;i<=m;++i) scanf("%d%d%lf",&e[i].x,&e[i].y,&e[i].w);
   		ans=getans(0);
   		for(RI i=1;i<=2500;++i) ans=min(getans(i),ans);
   		printf("Case %d: %.2lf\n",++kas,ans/(db)(n-1));
   	}
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值