题目分析
我们发现选出的生成树的边权和最多也只有2500,我们枚举选出的生成树边权和,于是就知道了平均数 w ˉ \bar{w} wˉ。然后将每条边的权值改为 ( w i − w ˉ ) 2 (w_i-\bar{w})^2 (wi−wˉ)2,做最小生成树。
这时候你可能会发问,为什么这样是对的,万一你在枚举了某个边权和后,出现了两种方案,一种边权和和我枚举的相同但是不优,另一种边权和不同但是更优,这样我们的做法就错了啊。
我们设想一下我们有一个向量a ( w 1 , w 2 , . . . w n − 1 ) (w_1,w_2,...w_{n-1}) (w1,w2,...wn−1)和一个向量b ( y , y , . . . y ) (y,y,...y) (y,y,...y),我们要最小化 ∑ i = 1 n − 1 ( w i − y ) 2 \sum_{i=1}^{n-1} (w_i-y)^2 ∑i=1n−1(wi−y)2,也就是要最小化|a-b|,画画图可以知道,当a与b垂直的时候最优。
而又因为b= y ( 1 , 1 , . . . 1 ) y(1,1,...1) y(1,1,...1),我们求一下a在 ( 1 , 1 , . . . 1 ) (1,1,...1) (1,1,...1)上的投影,即可算出最优的 y y y,这个用点积搞一下,得到投影的长度 y n = ∑ i = 1 n − 1 w i n y\sqrt{n}=\frac{\sum_{i=1}^{n-1} w_i}{\sqrt{n}} yn=n∑i=1n−1wi,即当 y = ∑ i = 1 n − 1 w i n y=\frac{\sum_{i=1}^{n-1} w_i}{n} y=n∑i=1n−1wi,也就是平均值的时候,是最优的。
所以如果我们枚举的边权和与我们求出最小生成树中那些边的边权和不一样,一定不优,不会影响解。
代码
#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;
}