1 条题解

  • 0
    @ 2026-2-18 10:37:10
    #include<bits/stdc++.h>
    using namespace std;
    
    // [1] 全局变量:测试用例总数t,村庄数量n,电力线路总数e
    int t, n, e;
    
    // [2] 全局结构体与数组:定义边的存储结构,arr存储所有电力线路
    struct Edge
    {
        int a, b, k; // 线路的两个端点村庄a、b,重建该线路的花费k
    }arr[150000];
    
    // [3] 全局数组:并查集父节点数组,存储每个村庄的父节点编号
    int fa[510];
    // [4] 全局数组:并查集深度数组,用于按秩合并优化
    int dep[510];
    
    // [12] 跳转至cmp函数:定义边的排序规则
    bool cmp(Edge x, Edge y)
    {
        // [13] 定义cmp函数的参数x、y:待比较的两条边
        return x.k < y.k; // [14] 按重建花费升序排列,优先选择成本更低的线路
    }
    // [15] cmp函数注释完成,返回主函数
    
    // [24] 跳转至find函数:查找节点x所在集合的根节点
    int find(int x)
    {
        // [25] 定义find函数的参数x:待查找根节点的村庄编号
        // [26] 循环向上遍历,直到找到父节点等于自身的根节点
        while(x != fa[x])
    	{
    		x = fa[x];
    	}
    	return x; // [27] 返回找到的根节点
    }
    // [28] find函数注释完成,返回主函数
    
    // [33] 跳转至unite函数:合并x和y所在的两个集合
    void unite(int x, int y)
    {
        // [34] 定义unite函数的参数x、y:待合并的两个村庄编号
        x = find(x); // [35] 查找x的根节点
        y = find(y); // [36] 查找y的根节点
        if (x == y) return; // [37] 若根节点相同,说明已连通,无需合并
    
        // [38] 按秩合并的if-else块:将深度更小的树合并到深度更大的树下,保持树的平衡
        if(dep[x] > dep[y])
        {
            fa[y] = x;
        }else if(dep[x] < dep[y])
        {
            fa[x] = y;
        }else
        {
            fa[y] = x;
            dep[x]++;
        }
    }
    // [39] unite函数注释完成,返回主函数
    
    // [5] 主函数:程序运行入口
    int main()
    {
        cin >> t; // [6] 读入测试用例的总组数
        // [7] 循环处理每一组测试用例,处理完一组t减1
        while(t--)
        {
            cin >> n >> e; // [8] 读入当前测试用例的村庄数n、电力线路数e
            // [9] 循环读入e条电力线路的端点和重建花费信息
            for(int i = 0; i < e; i++)
                cin >> arr[i].a >> arr[i].b >> arr[i].k; // [10] 读入单条线路的a、b、k
    
            // [11] 调用sort函数对边数组排序,排序规则由cmp定义,跳转至cmp函数
            sort(arr, arr + e, cmp);
    
            // [15] 回到主函数sort之后的代码
            // [16] 循环初始化并查集的父节点数组和深度数组
            for(int i = 0; i <= n; i++)
            {
                fa[i] = i; // [17] 每个村庄初始独立成一个集合,父节点设为自身
                dep[i] = 1; // [18] 每个集合的初始深度设为1
            }
    
            // [19] 定义核心统计变量
            int sum = 0, len = 0; // sum:重建电路的最小总花费;len:已选入生成树的有效边数
            // [20] 循环遍历排序后的边,构建最小生成树
            for(int i = 0; i < e && len < n-1; i++)
            {
                int a = arr[i].a; // [21] 获取当前边的起点村庄编号a
                int b = arr[i].b; // [22] 获取当前边的终点村庄编号b
                // [23] 调用find函数查找a和b的根节点,判断是否连通,跳转至find函数
                if(find(a) == find(b)) continue;
    
                // [28] 回到主函数if判断之后的代码
                // [29] 若a和b已连通,跳过当前边,避免生成环
                sum += arr[i].k; // [30] 累加当前边的重建花费到总花费sum
                len++; // [31] 已选入生成树的有效边数len加1
                // [32] 调用unite函数合并a和b所在的集合,跳转至unite函数
                unite(a, b);
            }
            // [39] 回到主函数遍历边循环之后的代码
            cout << sum << endl; // [40] 输出当前组的最小重建总花费sum
        }
        return 0; // [41] 主函数返回0,程序正常结束
    }
    
    • 1

    信息

    ID
    1358
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    6
    已通过
    1
    上传者