7-7 公路村村通(题解)

168 阅读2分钟

 

作者 陈越

单位 浙江大学

现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本,求使每个村落都有公路连通所需要的最低成本。

输入格式:

输入数据包括城镇数目正整数N(≤1000)和候选道路数目M(≤3N);随后的M行对应M条道路,每行给出3个正整数,分别是该条道路直接连通的两个城镇的编号以及该道路改建的预算成本。为简单起见,城镇从1到N编号。

输出格式:

输出村村通需要的最低成本。如果输入数据不足以保证畅通,则输出−1,表示需要建设更多公路。

输入样例:

6 15
1 2 5
1 3 3
1 4 7
1 5 4
1 6 2
2 3 4
2 4 6
2 5 2
2 6 6
3 4 6
3 5 1
3 6 1
4 5 10
4 6 8
5 6 3

输出样例:

12

个人思路:

这是一道最小生成树问题,对于最小生成树问题,当图是稠密图时,我们选择prim算法,此题是稀疏图,我们可以用Kruskal算法。

Kruskal算法:

将所有可能的边从小到大排序,遍历这些边,如果此边连接的两个城镇已经有道路了,我们继续遍历,没有道路时,将这个路径加入最小生成树,并将这两个城镇连通。

判断城市是否连通,我们可以选择并查集实现。

并查集可以看我这篇文章:7-2 并查集【模板】(题解)_星河边采花的博客-CSDN博客

AC代码:(带注释解释)

#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 1010, M = 3030, INF = 0x3f3f3f3f;

int n, m;
int p[N];  //用于并查集的数组

struct Edge  //定义边,和它链接的两个城镇
{
    int a, b, w;

    bool operator< (const Edge& W)const  //小于号的重载,方便排序,不喜欢重载的可以写cmp函数
    {
        return w < W.w;
    }
}edges[M];

int find(int x)
{
    if (p[x] != x) p[x] = find(p[x]);  //并查集的查找
    return p[x];
}

int kruskal()
{
    sort(edges, edges + m);   //首先对边排序

    for (int i = 1; i <= n; i++) p[i] = i;   //并查集初始化

    int res = 0, cnt = 0;
    for (int i = 0; i < m; i++)
    {
        int a = edges[i].a, b = edges[i].b, w = edges[i].w;

        a = find(a), b = find(b);
        if (a != b)            //如果城镇不相连
        {
            p[a] = b;
            res += w;
            cnt++;
        }
    }

    if (cnt < n - 1) return INF;  //如果连接城镇的次数少于n-1,说明不能使得每个城镇都相连。
    return res;
}

int main()
{
    scanf("%d%d", &n, &m);

    for (int i = 0; i < m; i++)
    {
        int a, b, w;
        scanf("%d%d%d", &a, &b, &w);
        edges[i] = { a, b, w };
    }

    int t = kruskal();

    if (t == INF) puts("-1");
    else printf("%d\n", t);

    return 0;
}