题意:给定带权无向图,求出一颗方差最小的生成树。
额外信息:
整数U,V,W,代表连接U,V的边,和权值W。保证图连通。
1<=U,V<=N<=50, N-1<=M<=1000,0<=W<=50。
思路(转载):
要使方差最小,则使 Σ(wi-平均数)^2最小即可。
因为权值的范围很小,所以我们可以枚举这个平均数,每次把边权赋成(wi-平均数)^2,做kruscal。
但是,我们怎么知道枚举出来的平均数是不是恰好是我们的这n-1条边的呢? 就在更新答案的时候加个特判就行了。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1005;
struct edge{
int a,b,w;
double ww;
bool operator<(const edge &x)const{
return w<x.w;
}
}e[N];
bool cmp(edge& a,edge& b){
return a.ww<b.ww;
}
int p[N];
int find(int x){
if(p[x]!=x) p[x]=find(p[x]);
return p[x];
}
int main(){
int n,m,cnt=0;
while(scanf("%d%d",&n,&m)!=EOF&&n){
for(int i=0;i<m;++i) scanf("%d%d%d",&e[i].a,&e[i].b,&e[i].w);
sort(e,e+m);
int maxn=0,minn=0;
for(int i=0;i<n-1;++i) minn+=e[i].w;
for(int j=m-1;j>=m-n+1;--j) maxn+=e[j].w;
double ans=1e5;
for(int k=minn;k<=maxn;++k){//n-1条边的和的范围是[min,max]
for(int i=0;i<m;++i) p[i]=i;//每做一次kruscal都要初始化并查集
int sum=0;
double res=0;
double t=k*1.0/(n-1);//这个和k对应的平均值
for(int i=0;i<m;++i) e[i].ww=(e[i].w-t)*(e[i].w-t);
sort(e,e+m,cmp);//对边按e[i].ww重排序
for(int i=0;i<m;++i){
int a=e[i].a,b=e[i].b,w=e[i].w;
a=find(a),b=find(b);
if(a!=b){
p[a]=b;
sum+=w;//记录这些边的和 最后特判用
res+=e[i].ww;
}
}
if(sum==k) ans=min(ans,res);//特判
}
printf("Case %d: %.2lf\n",++cnt,ans/(n-1));
}
}
碎碎念:
听说是蓝桥杯的题,蓝桥杯现在都这么难了吗(⊙o⊙)!
这个题解三句话讲得明明白白,真的厉害,关注了!
还有不用重排序的【bzoj3754】Tree之最小方差树 最小生成树 我还没看。。。 有空看。。