[bzoj3754]Tree之最小方差树【暴力】【MST】

【题目描述】

Description

Wayne在玩儿一个很有趣的游戏。在游戏中,Wayne建造了N个城市,现在他想在这些城市间修一些公路,当然并不是任意两个城市间都能修,为了道路系统的美观,一共只有M对城市间能修公路,即有若干三元组 (Ui,Vi,Ci)表示Ui和Vi间有一条长度为Ci的双向道路。当然,游戏保证了,若所有道路都修建,那么任意两城市可以互相到达。Wayne拥有恰好N-1支修建队,每支队伍能且仅能修一条道路。当然,修建长度越大,修建的劳累度也越高,游戏设定是修建长度为C的公路就会有C的劳累度。当所有的队伍完工后,整个城市群必须连通,而这些修建队伍们会看看其他队伍的劳累情况,若劳累情况差异过大,可能就会引发骚动,不利于社会和谐发展。Wayne对这个问题非常头疼,于是他想知道,这N1支队伍劳累度的标准差最小能有多少。
标准差的定为:设有N个数,分别为ai,它们的平均数为 ,那么标准差就是

Input

第一行两个正整数N,M
接下来M行,每行三个正整数Ui,Vi,Ci

Output

输出最小的标准差,保留四位小数。

Sample Input

3 3
1 2 1
2 3 2
3 1 3

Sample Output

0.5000

HINT

N<=100,M<=2000,Ci<=100

Source

【题解】

 把标准差看成方差Wa了无数发。。

对于任意一种选取方案,若把 a的平均值 看做自变量x,暴力展开可知:当 x==a的平均值 时,标准差取到最小值。

因此可以枚举 a的平均值 。

若每隔 1/(n-1) 枚举一次, 复杂度太高无法通过。

但由于 a 为整数,所以对于两条边权值分别为 ax, ay(ax<ay), 当枚举 [ax..mid) 或 (mid..r] 时,选取方案始终相同。

因此只要枚举在两边的情况和中间的情况。

具体来说,只要每隔0.25枚举一次。

/* --------------
    user Vanisher
    problem bzoj-3754 
----------------*/
# include <bits/stdc++.h>
# define 	ll 		long long
# define 	M 		2010
# define 	N 		110
using namespace std;
int read(){
	int tmp=0, fh=1; char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') fh=-1; ch=getchar();}
	while (ch>='0'&&ch<='9'){tmp=tmp*10+ch-'0'; ch=getchar();}
	return tmp*fh;
}
struct node{
	int u,v,w;
	double vote;
}e[M];
int f[N],n,m;
int dad(int x){
	if (f[x]==x) return x;
		else return f[x]=dad(f[x]);
}
double sqr(double x){
	return x*x;
}
bool cmp(node x, node y){
	return x.vote<y.vote;
}
double check(double x){
	for (int i=1; i<=m; i++)
		e[i].vote=sqr(e[i].w-x);
	sort(e+1,e+m+1,cmp);
	for (int i=1; i<=n; i++) f[i]=i;
	int num=n,i=0;
	double sum=0,now;
	while (num>1){
		i++; int u=dad(e[i].u), v=dad(e[i].v);
		if (u!=v){
			num--;
			sum=sum+e[i].w;
			f[u]=v;
		}
	}
	now=sum/(n-1); sum=0; num=n;
	for (int i=1; i<=n; i++) f[i]=i;
	i=0;
	while (num>1){
		i++; int u=dad(e[i].u), v=dad(e[i].v);
		if (u!=v){
			num--;
			sum=sum+sqr(e[i].w-now);
			f[u]=v;
		}
	}
	return sqrt(sum/(n-1));
}
int main(){
	n=read(), m=read(); 
	double now=0,ans,ansnow;
	for (int i=1; i<=m; i++){
		e[i].u=read(), e[i].v=read(), e[i].w=read();
		now=now+e[i].w;
	} 
	now=now/m,ans=ansnow=check(now);
/*	for (double T=30; T>0.1; T=T*0.9998){
		int fh=rand()%2; if (fh==0) fh--;
		double tmp=rand()*T/32767*fh+now,anstmp=check(tmp);
		ans=min(ans,anstmp);
		if (anstmp<ansnow)
			now=tmp, ansnow=anstmp;
			else if (exp((ansnow-anstmp)/T)>rand()*1.0/32767)
				now=tmp, ansnow=anstmp;
	}*/
	double eps=1.0/(m-1);
	for (double T=0; T<=100; T+=0.25)
		ans=min(ans,check(T));
	printf("%.4lf\n",ans);
	return 0;
}
 一开始写的退火,知识水平不够根本过不去。。。

posted @ 2018-01-27 19:55  Vanisher  阅读(130)  评论(0编辑  收藏  举报