最小方差生成树

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

 

额外信息:

整数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之最小方差树 最小生成树 我还没看。。。 有空看。。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值