洛谷

洛谷题单指南-最小生成树-P1550 [USACO08OCT] Watering Hole G

原题链接:https://www.luogu.com.cn/problem/P1550

题意解读:挖n个井,每个井i直接挖的代价是w[i],如果从一个已有井i挖一条路到j,则开通井j的代价是p[i][j],求挖所有井的最小代价。

解题思路:

井可以看做图中的节点,直接挖的代价是点权值,从已开通的井挖一条路到待开通的井代价是边权,既有点权又有边权不易处理。

可以设定一个虚拟源点0,假设0号井已开通,连n条边到所有节点,边权值都是每个节点的w,表示从0号点挖一条路到各个点的代价。

要计算挖通所有点的最小代价,就是在所有路径中求最小生成树。

100分代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 305;

struct Edge
{
    int u, v, w;
} edges[N * N];
int cnt;
int p[N];
int n, ans;

bool cmp(Edge a, Edge b)
{
    return a.w < b.w;
}

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

void kruskal()
{
    sort(edges + 1, edges + cnt + 1, cmp);
    for(int i = 0; i <= n; i++) p[i] = i; //0 ~ n的节点
    for(int i = 1; i <= cnt; i++)
    {
        int a = find(edges[i].u);
        int b = find(edges[i].v);
        if(a != b)
        {
            p[a] = b;
            ans += edges[i].w;
        }
    }
}

int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++)
    {
        int w;
        cin >> w;
        cnt++;
        edges[cnt].u = 0, edges[cnt].v = i, edges[cnt].w = w;
    }
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= n; j++)
        {
            int x;
            cin >> x;
            if(x != 0)
            {
                cnt++;
                edges[cnt].u = i, edges[cnt].v = j, edges[cnt].w = x;
            }
        }
    }
    kruskal();
    cout << ans;
    return 0;
}