题解:洛谷 P1550 [USACO08OCT] Watering Hole G

【题目来源】

洛谷:[P1550 USACO08OCT] Watering Hole G - 洛谷

【题目描述】

Farmer John 的农场缺水了。

他决定将水引入到他的 \(n\) 个田地。他准备通过挖若干井,并在各块田中修筑水道来连通各块田地以供水。在第 \(i\) 号田中挖一口井需要花费 \(W_i\) 元。连接 \(i\) 号田与 \(j\) 号田需要 \(P_{i,j}\)\(P_{j,i}=P_{i,j}\))元。

请求出 FJ 需要为使所有田地都与有水的田地相连或拥有水井所需要的最少钱数。

【输入】

第一行为一个整数 \(n\)

接下来 \(n\) 行,每行一个整数 \(W_i\)

接下来 \(n\) 行,每行 \(n\) 个整数,第 \(i\) 行的第 \(j\) 个数表示连接 \(i\) 号田和 \(j\) 号田需要的费用 \(P_{i,j}\)

【输出】

输出最小开销。

【输入样例】

4
5
4
4
3
0 2 2 2
2 0 3 3
2 3 0 4
2 3 4 0

【输出样例】

9

【算法标签】

生成树#

【代码详解】

#include <bits/stdc++.h>
using namespace std;
const int N = 305, M = 2 * 100005, INF = 1e9;
int n, cur;  // n: 节点数, cur: 边计数器
int h[N], e[M], w[M], ne[M], idx;  // 邻接表
int p[N];  // 并查集

struct Edge
{
    int u, v, cost;  // 起点u,终点v顶点编号,cost,边u-v的权值
    bool operator < (const Edge &E) const  // 重载<操作符,按cost从小到大排序
    {
        return cost < E.cost;
    }
} edges[M];

int find(int x)  // 并查集查找
{
    if (p[x] != x)
    {
        p[x] = find(p[x]);
    }
    return p[x];
}
int kruskal()  // Kruskal算法求最小生成树
{
    sort(edges + 1, edges + 1 + cur);  // 边按权值排序
    for (int i = 1; i <= n; i++)  // 初始化并查集
    {
        p[i] = i;
    }
    int res = 0, cnt = 0;  // res: 总权值, cnt: 已选边数
    for (int i = 1; i <= cur; i++)  // 遍历所有边
    {
        int u = edges[i].u, v = edges[i].v, w = edges[i].cost;
        u = find(u), v = find(v);
        if (u != v)  // 不连通,加入生成树
        {
            p[u] = v;
            res += w;
            cnt++;
        }
    }
    if (cnt < n - 1)  // 如果边数不足n-1,图不连通
    {
        return INF;
    }
    return res;  // 返回最小生成树权值
}

int main()
{
    cin >> n;  // 输入节点数
    
    memset(h, -1, sizeof(h));
    for (int i = 1; i <= n; i++)  // 添加虚拟节点0的边
    {
        int x;
        cin >> x;
        edges[++cur] = {0, i, x};  // 节点0到节点i
        edges[++cur] = {i, 0, x};  // 节点i到节点0
    }
    for (int i = 1; i <= n; i++)  // 添加节点间的边
    {
        for (int j = 1; j <= n; j++)
        {
            int x;
            cin >> x;
            edges[++cur] = {i, j, x};  // 节点i到节点j
            edges[++cur] = {j, i, x};  // 节点j到节点i
        }
    }
    
    cout << kruskal();  // 输出最小生成树权值
    return 0;
}

【运行结果】

4
5
4
4
3
0 2 2 2
2 0 3 3
2 3 0 4
2 3 4 0
9
posted @ 2026-04-01 10:43  团爸讲算法  阅读(1)  评论(0)    收藏  举报