1. 程式人生 > >bzoj3080 Minimum Variance Spanning Tree 最小方差生成樹

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

題目分析

我們發現選出的生成樹的邊權和最多也只有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;
}