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;
}