noip.ac 3286

给定带权无向图,求出一颗方差最小的生成树。

_________________________________________________

方差就是各个数的平均值与各个数的差的平方的和的平均值。

sum((a_i-ave)^2)/n

多组数据。

枚举是不现实的。但是不枚举如何知道他们的平均值?

把所有的边排序,取出最小的n-1条和最大的n-1条分别求和,为mn和mx。那么ave*(n-1)一定在这个范围内。

枚举这个值(也就是n-1条边的和),除以n-1就得到了平均值,用图中所有的变用这个平均值求差值的平方作为变得新权ww。用ww求新的最小生成树。

开始,怀疑过:有些和是不会产生的,那么他们产生的方差会不会影响最终结果。

假设,和为s时产生了平均数ave,用它选了相应最小的n-1条树边并产生的对应的方差。但是,这个和s并不会产生,当然ave也就不会产生。

我们假设,对应的n-1条边真正产生的平均值是AVE,那么很明显AVE要比ave也好,当枚举到对应的值时自然会得到更好的解!

_________________________________________________

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=55;
 4 const int maxm=1010;
 5 struct edge
 6 {
 7     int u,v;
 8     double w,ww;
 9 }e[maxm];
10 int n,m,mn,mx;
11 bool cmp(edge a,edge b)
12 {
13     return a.w<b.w;
14 }
15 bool cmp2(edge a,edge b)
16 {
17     return a.ww<b.ww;
18 }
19 double ans;
20 int cas;
21 int f[maxn];
22 int find(int x)
23 {
24     return f[x]==x?x:f[x]=find(f[x]);
25 }
26 void join(int x,int y)
27 {
28     x=find(x);y=find(y);
29     if(rand()%2)f[x]=y;
30     else f[y]=x;
31 }
32 int main()
33 {
34     while(scanf("%d%d",&n,&m)==2 && n &&m)
35     {
36         cas++;
37         for(int i=1;i<=m;++i)scanf("%d%d%lf",&e[i].u,&e[i].v,&e[i].w);
38         sort(e+1,e+1+m,cmp);
39         mn=mx=0;
40         ans=100000000000;
41         for(int i=1;i<n;++i)mn+=e[i].w;
42         for(int i=m;i>m-n+1;--i)mx+=e[i].w;
43         for(int i=mn;i<=mx;++i)
44         {
45             double tp=0;
46             double pj=1.0*i/(n-1);
47             for(int j=1;j<=m;++j)e[j].ww=(e[j].w-pj)*(e[j].w-pj);
48             for(int j=1;j<=n;++j)f[j]=j;
49             sort(e+1,e+1+m,cmp2);
50             int js=0;
51             for(int j=1;j<=m;++j)
52             {
53                 if(find(e[j].u)!=find(e[j].v))
54                 {
55                     join(e[j].u,e[j].v);
56                     js++;
57                     tp+=e[j].ww;
58                     if(js==n-1)break;
59                 }
60             }
61             ans=min(ans,tp);
62         }
63         printf("Case %d: %.2lf
",cas,ans/(n-1));
64     }
65     return 0;
66 }
View Code
原文地址:https://www.cnblogs.com/gryzy/p/14514027.html