最小方差生成树

输入格式
输入多组测试数据。第一行为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组。
思路:将方差作为边的权值来套入迪杰斯特拉算法,问题是方差需要求得边权值之和,但是边权值之和貌似是结果的部分,怎么解决呢,考虑到边权值已知边权值之和是个比较小的范围,所以可以枚举边权值之和计算方差,逃入迪杰斯特拉算法。

迪杰斯特拉算法:迪杰斯特拉最最朴素的思想就是按长度递增的次序产生最短路径。即每次对所有可见点的路径长度进行排序后,选择一条最短的路径,这条路径就是对应顶点到源点的最短路径。

算法就干了两件事:

【1】不断运行广度优先算法找可见点,计算可见点到源点的距离长度

【2】从当前已知的路径中选择长度最短的将其顶点加入S作为确定找到的最短路径的顶点。

下面是例题代码:

#include<bits/stdc++.h>
using namespace std;
int n,m;
double ans;
int tmp[1001];
int fa[1001]; 
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)//自定义的比较函数方便使用sort函数
{
	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()
{
	int cas=1;
	while(~scanf("%d%d",&n,&m),n+m)
	{
		int maxx=0,minn=0;
		ans=999999999999999.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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值