[最小生成树] [克鲁斯卡尔]走廊泼水节

版权声明:原创博客,转载请标明出处! 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;
}

猜你喜欢

转载自blog.csdn.net/caipengbenren/article/details/89526733