给定一棵N个节点的树,要求增加若干条边,把这棵树扩充为完全图,并满足图的唯一最小生成树仍然是这棵树。
求增加的边的权值总和最小是多少。
题解:
这一题的关键在于如何处理加边这一步。首先,我们要了解最小生成数如何构成,相比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;
}