https://www.acwing.com/problem/content/description/348/
给定一棵N个节点的树,要求增加若干条边,把这棵树扩充为完全图,并满足图的唯一最小生成树仍然是这棵树。
求增加的边的权值总和最小是多少。
注意: 树中的所有边权均为整数,且新加的所有边权也必须为整数。
输入格式
第一行包含整数t,表示共有t组测试数据。
对于每组测试数据,第一行包含整数N。
接下来N-1行,每行三个整数X,Y,Z,表示X节点与Y节点之间存在一条边,长度为Z。
输出格式
每组数据输出一个整数,表示权值总和最小值。
每个结果占一行。
数据范围
1≤N≤6000
1≤Z≤100
输入样例:
2
3
1 2 2
1 3 3
4
1 2 3
2 3 4
3 4 5
输出样例:
4
17
做法:利用kruskal算法,每次将两个连通块合并时(两个连通块时完全图),算出将这两个连通块合并时构成完全图的最小代价。
维护每个集合的数量num[];
即:ans += (num[f[x]]num[f[y]]-1)(w+1); num[f[x]] = num[f[x]] + num[f[y]];
ac代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 6005;
const int M = 6005;
struct Node{
int a,b,w;
bool operator < (const Node & t) const {
return w<t.w;
}
}g[M];
int f[N],num[N];
int n,m,T;
int find(int x){
if(x != f[x])
f[x] = find(f[x]);
return f[x];
}
//将y合并到集合x中
void merge(int x,int y){
x = find(x);y = find(y);f[y] = x;
}
int ans ;
void kru(){
int i,j;
for(i = 1;i<=n;i++) f[i] = i;
for(i = 1;i<=n;i++) num[i] = 1;//初始化每个集合的数量
sort(g+1,g+1+m);
for(i = 1;i<=m;i++){
int x = g[i].a,y = g[i].b,w = g[i].w;
if(find(x) == find(y)) continue;
//两个集合合并
ans += (num[f[x]]*num[f[y]]-1)*(w+1);
num[f[x]] = num[f[x]] + num[f[y]];
merge(x,y);
}
}
int main(){
int i,j;
cin>>T;
while(T--){
cin>>n;
ans = 0;
m = n-1;
for(i = 1;i<=m;i++){
cin>>g[i].a>>g[i].b>>g[i].w;
}
kru();
cout<<ans<<endl;
}
return 0;
}