[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
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; }一开始写的退火,知识水平不够根本过不去。。。