0
点赞
收藏
分享

微信扫一扫

最小方差生成树 (Kruskal)



问题描述

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


输入格式


输入多组测试数据。第一行为N,M,依次是点数和边数。接下来M行,每行三个整数U,V,W,代表连接U,V的边,和权值W。保证图连通。n=m=0标志着测试文件的结束。


输出格式


对于每组数据,输出最小方差,四舍五入到0.01。输出格式按照样例。


样例输入


4 5
1 2 1
2 3 2
3 4 2
4 1 1
2 4 3
4 6
1 2 1
2 3 2
3 4 3
4 1 1
2 4 3
1 3 3
0 0


样例输出


Case 1: 0.22
Case 2: 0.00


数据规模与约定


1<=U,V<=N<=50,N-1<=M<=1000,0<=W<=50。数据不超过5组。

题解:首先说明LQB这题数据好像有错啊....这题有很多姿势。原题来自是BZOJ 3754 (VIP题).....

BZOJ那道是算标准差,这题是方差。姿势差不多一样。

对于这道题来说,可以这样做,因为求方差最小,那么就是要求每条边的(权重-边权和的平均值)^2的和最小。

那么我们枚举全部边权和的可能值,每次都跑一次Kruskal,跑的时候以(权重-边权和的平均值)^2为当前权重,然后如果我们先前枚举的的边权和等于跑完Kruskal后,生成树的权值。我们就更新答案就可以了。


代码:


#include<bits/stdc++.h>
using namespace std;
int n,m;
double ans;
int tmp[100100];
int fa[100100];
struct edge
{
int x,y;
double w,val;
edge(int x=0,int y=0,double w=0,double val=0):x(x),y(y),w(w),val(val){}
}e[100100];

int cmp(edge a,edge b)
{
return a.val<b.val;
}
int getfather(int x)
{
return fa[x]==x ? x: fa[x]=getfather(fa[x]);
}

void kruskal(int sum)
{

int cnt=n;
double res=0;
double tmp_ans=0;

double average=sum * 1.0 /(n-1);

//方差 * (n-1)
for(int i=0;i<m;i++){
e[i].val = (e[i].w-average) * (e[i].w-average);
}
for(int i=1;i<=n;i++)fa[i]=i;
sort(e,e+m,cmp);
for(int i=0;i<m;i++)
{
int t1=getfather(e[i].x);
int t2=getfather(e[i].y);
if(t1!=t2)
{
fa[t1]=t2;
res += e[i].w;
tmp_ans += e[i].val;
cnt--;
if(cnt==1)break;
}
}
if((int)res==sum){
ans=min(ans, tmp_ans);
//cout<<"true"<<endl;
}
}
int main()
{
// freopen("in.txt","r",stdin);
int cas=1;
while(~scanf("%d%d",&n,&m),n+m)
{
int maxx=0,minn=0;
ans=99999999999.0;

for(int i=0;i<m;i++){
cin>>e[i].x>>e[i].y>>e[i].w;
tmp[i]=e[i].w;
}
sort(tmp,tmp+m);
for(int i=0;i<n-1;i++){
minn+=tmp[i];
}
for(int i=m-1;i>m-n;--i){
maxx+=tmp[i];
}
for(int i=minn;i<=maxx;i++){ //
kruskal(i);
}
ans=ans/(n-1);
printf("Case %d: %.2f\n",cas++,ans);
}
return 0;
}



举报

相关推荐

0 条评论