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

版权声明:这篇文章的作者是个蒟蒻,没有转载价值,如果要转说一下好了 https://blog.csdn.net/litble/article/details/82940082

题目分析

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

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

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

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

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

代码

#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;
}

猜你喜欢

转载自blog.csdn.net/litble/article/details/82940082