【BZOJ 3754】Tree / T4 / Tree之最小方差树(最小生成树)(模拟)

168 篇文章 1 订阅
123 篇文章 2 订阅

Tree / T4 / Tree之最小方差树

题目链接:BZOJ 3754

题目大意

给你一些边,要你找到一个生成树,使得树上边权的方差最小。

思路

首先想想方差,它就是要每个数竟可能贴近平均数,那我们考虑枚举这个平均数。
(一开始把边按边权从小到大排序)
然后左右指针移动,不断选靠近平均数的边,用最小生成树的方法搞出生成树,然后对这个生成树算答案。
(然后注意这个平均数不能用枚举的那个,要重新算)

然后你发现你枚举平均数会超时,你考虑枚举的两个平均数之间的相差的可以适当增大。
那可以是多少呢?
观察到边权都是整数,那你如果小数部分是小于 0.5 0.5 0.5 的话是不会影响到选边的结果的。
0.5 0.5 0.5 两边都可以会随机选?所以不太好,超过 0.5 0.5 0.5 会可能导致两边选的顺序不一样,然后小于 0.5 0.5 0.5 而且倍数一定会覆盖每个整数的最大的就是 0.25 0.25 0.25 了)

然后就可以了。

代码

#include<cmath>
#include<cstdio>
#include<algorithm>

using namespace std;

struct node {
	int x, y, z;
}a[2001];
int n, m, fa[101], kn;
int tmp[101];
double ans;

bool cmp(node x, node y) {
	return x.z < y.z;
}

int find(int now) {
	if (fa[now] == now) return now;
	return fa[now] = find(fa[now]);
}

void clac() {
	double pj = 0;
	for (int i = 1; i <= n - 1; i++)
		pj += 1.0 * tmp[i];
	pj = pj / (1.0 * (n - 1));
	double an = 0;
	for (int i = 1; i <= n - 1; i++) {
		an = an + (1.0 * tmp[i] - pj) * (1.0 * tmp[i] - pj);
	}
	an = an / (1.0 * (n - 1));
	ans = min(ans, sqrt(an));
}

int main() {
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= m; i++) {
		scanf("%d %d %d", &a[i].x, &a[i].y, &a[i].z);
	}
	
	sort(a + 1, a + m + 1, cmp);
	
	ans = 1000000000.0;
	int ll = 0;
	for (double st = 0; st <= 100.0; st += 0.25) {
		for (int i = 1; i <= n; i++) fa[i] = i;
		while (1.0 * a[ll + 1].z <= st && ll < m) ll++;
		kn = 0;
		int l = ll, r = l + 1, i;
		while (kn != n - 1) {
			if (l == 0) i = r, r++;
				else if (r == m + 1) i = l, l--; 
					else {
						if (st - 1.0 * a[l].z <= 1.0 * a[r].z - st) i = l, l--;
							else i = r, r++;
					}
			int X = find(a[i].x), Y = find(a[i].y);
			if (X == Y) continue;
			kn++; tmp[kn] = a[i].z;
			fa[X] = Y;
			if (kn == n - 1) break;
			if (l == 0 && r == m + 1) break;
		}
		clac();
	}
	
	printf("%.4lf", ans);
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值