版权声明:原创博客,转载请标明出处! https://blog.csdn.net/caipengbenren/article/details/89526733
走廊泼水节
给定一棵N个节点的树,要求增加若干条边,把这棵树扩充为完全图,并满足图的唯一最小生成树仍然是这棵树。
求增加的边的权值总和最小是多少。
输入格式
第一行包含整数t,表示共有t组测试数据。
对于每组测试数据,第一行包含整数N。
接下来N-1行,每行三个整数X,Y,Z,表示X节点与Y节点之间存在一条边,长度为Z。
输出格式
每组数据输出一个整数,表示权值总和最小值。
每个结果占一行。
数据范围
N≤6000,Z≤100
输入样例:
2
3
1 2 2
1 3 3
4
1 2 3
2 3 4
3 4 5
输出样例:
4
17
这一题的关键在于如何处理加边这一步。首先,我们要了解最小生成数如何构成,相比prime算法这一题更适合使用克鲁斯卡尔算法。
在构造最小树的时候,我们需要整合两个集合,克鲁斯卡尔算法是选择生序排序的边依次添加,为了保证题目中所要求的唯一最小生成树,所以我们要保证,当前的边一定是两个集合中任意两个点之间的边长最小的,同时又要使得权值总和最小,所以整合后增加的权值为(size[x] * size[y] - 1)*(cp[i].z + 1).
整合的同时维护一个size数组即可。
#include<bits/stdc++.h>
using namespace std;
struct node {
int x, y, z;
bool operator<(const node &tem)const{
return this->z < tem.z;
}
}cp[6001];
int f[6001], size[6001], ans = 0;
int t, n;
void init() {
for (int i = 1; i <= n; i++) {
f[i] = i;
size[i] = 1;
}
}
int find(int i) {
if (f[i] != i) {
return f[i] = find(f[i]);
} else {
return i;
}
}
void Union() {
for (int i = 0; i < n - 1; i++) {
int f_x = find(cp[i].x);
int f_y = find(cp[i].y);
if (f_x != f_y) {
f[f_x] = f_y;
ans += (size[f_x] * size[f_y] - 1) * (cp[i].z + 1);
size[f_y] += size[f_x];
}
}
}
int main () {
cin >> t;
while(t--) {
cin >> n;
init();
for (int i = 0; i < n - 1; i++) {
cin >> cp[i].x >> cp[i].y >> cp[i].z;
}
ans = 0;
sort(cp, cp + n - 1);
Union();
cout << ans << "\n";
}
return 0;
}