走廊泼水节(最小生成树定理)⭐

题面

给定一棵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 

题解

这道题考的是最小生成树定理

定理
任意一颗最小生成树一定包含无向图中权值最小的边

证明(反证法)

假设G = (V, E)存在一颗最小生成树不包含权值最小边(x, y, z),
将(x, y, z)加入图中,则出现环, 最小生成树, 不能有环,
所以要从这个环中删去一条边(当然能树还是联通的),
这个环中任一条边都比(x, y, z)大, 故肯定留下这条边, 故这颗生成树不是最小生成树

推论⭐

给定一张无向图G(V,E), n = |V|, m = |E|, 从E中选k< n - 1条边构成G的森林
若在从m = k条边中选(n - 1 - k)构成G的生成树,并且边权值最小,
则给生成树必定包含 m - k条边中连接最小生成树的边来连接森林的两棵树

故这道题就是不断选链接森林且是最小生成树的边

所以存在森林, 然后用体中的一条边(x, y, z)将 x节点 和 y节点 所在的树链接

因为要生成完全图,则着两棵树中的所有节点添加边权都要为 z + 1

不然如果小于等于z, 这条新添加的边链接了x和y所在的树, 且边权值最小, 则必定为最小生成树的边(和题意矛盾)

所以就是一边连边, 一边添加 z + 1边

实现细节见代码

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;++i)
using namespace std;
typedef long long ll;

const int maxn = 6005;

struct rec { int x, y, z; } e[maxn];

bool operator <(rec& a, rec& b)
{
    return a.z < b.z;
}


int _, n, fa[maxn], s[maxn];

int find(int x)
{
    if (x == fa[x]) return x;
    return fa[x] = find(fa[x]);
}

void unit(int x, int y)
{
    x = find(x), y = find(y);
    fa[y] = x, s[x] += s[y];
}

int main ()
{
    for (cin >> _; _; --_)
    {
        cin >> n; fa[n] = n, s[n] = 1;
        rep (i, 1, n - 1) 
            cin >> e[i].x >> e[i].y >> e[i].z, fa[i] = i, s[i] = 1;
        sort(e + 1, e + n);
        
        ll ans = 0;
        rep (i, 1, n - 1)
        {
            int x = find(e[i].x), y = find(e[i].y);
            if (x == y) continue;
            ans += (ll)(e[i].z + 1) * (s[x] * s[y] - 1);
            unit(x, y);
        }
        cout << ans << '
';
    }
    return 0;
}
原文地址:https://www.cnblogs.com/2aptx4869/p/12899024.html