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


    题面

    给定一棵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;
    }
    
  • 相关阅读:
    免费部署Woocall到您自己的网站上
    服务器控件开发之复杂属性
    删除数据库的所有存储过程、主键、外键、索引等
    怎样在dropdownlist的每一项前加一个或多个空格
    Java的内部类学习
    StringUtils全览 (转)
    Java异常大全
    Java web 开发小问题总结(持续更新中)
    Java常用方法总结(持续更新中)
    Python 常用函数
  • 原文地址:https://www.cnblogs.com/2aptx4869/p/12899024.html
Copyright © 2020-2023  润新知