还是畅通工程 - 九度 OJ 1017

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/LYKXHTP/article/details/88600993

还是畅通工程 - 九度 OJ 1017

题目

时间限制:1 秒 内存限制:128 兆 特殊判题:否
题目描述:
某省调查乡村交通状况,得到的统计表中列出了任意两村庄间的距离。省政府“畅通工程的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要间接通过公路可达即可),并要求铺设的公路总长度为最小。请计算最小的公路总长度。
输入:
测试输入包含若干测试用例。每个测试用例的第 1 行给出村庄数目 N ( < 100 );随后的 N(N-1)/2 行对应村庄间的距离,每行给出一对正整数,分别是两 个村庄的编号,以及此两村庄间的距离。为简单起见,村庄从1到N编号。当N为0时,输入结束,该用例不被处理。
输出:
对每个测试用例,在1行里输出最小的公路总长度。
样例输入:
3
1 2 1
1 3 2
2 3 4
4
1 2 1
1 3 4
1 4 1
2 3 3
2 4 2
3 4 5
0
样例输出:
3
5
来源:
2006年浙江大学计算机及软件工程研究生机试真题

在给定的道路中选取一些,使所有的城市直接或间接连通且使道路的总长度最小,该例即为典型的最小生成树问题。将城市抽象成图上的结点,将道路抽象成连接点的边,其长度即为边的权值。经过这样的抽象,求得该图的最小生成树,其上所有的边权和即为所求。

最小生成树Kruskal算法按如下步骤求解最小生成树:
1.初始时所有结点属于孤立的集合。
2.按照边权递增顺序遍历所有的边,若遍历到的边两个顶点仍分属不同的集合(该边即为连通这两个集合的边中权值最小的那条)则确定该边为最小生成树上的一条边,并将这两个顶点分属的集合合并。
3.遍历完所有边后,原图上所有结点属于同一个集合则被选取的边和原图中所有结点构成最小生成树;否则原图不连通,最小生成树不存在。

如步骤所示,在用Kruskal算法求解最小生成树的过程中涉及到大量的集合操作,可以使用并查集来实现这些操作。

#include <stdio.h>
#include <algorithm>
#define N 101

using namespace std;

int Tree[N];

int findRoot(int x){
    if(Tree[x]==-1)return x;
    else{
        int tmp=findRoot(Tree[x]);
        Tree[x]=tmp;
        return tmp;
    }
}

struct Edge{//边结构体
    int a,b;//边两个顶点的编号
    int cost;//该边的权值
    bool operator < (const Edge &A) const{
        //重载小于号使其可以按照边权从小到大排列
        return cost < A.cost;
    }
}edge[6000];

int main()
{
    int n;
    while(scanf("%d",&n)!=EOF && n!=0){
        for(int i=1;i<=n*(n-1)/2;i++){
            scanf("%d%d%d",&edge[i].a,&edge[i].b,&edge[i].cost);
        }
        sort(edge+1,edge+1+n*(n-1)/2);
        //按照边权值递增排列所有的边

        for(int i=1;i<=n;i++){
            Tree[i]=-1;//初始时所有节点都属于孤立的集合
        }
        int ans=0;//最小生成树上边权的和,初始值为0
        for(int i=1;i<=n*(n-1)/2;i++){
            //按照边权值递增顺序遍历所有的边
            int a=findRoot(edge[i].a);
            int b=findRoot(edge[i].b);
            //查找该边两个顶点的信息

            if(a!=b){
                //若它们属于不同集合,则选用该边
                Tree[a]=b;//合并两个集合
                ans+=edge[i].cost;
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

如代码所示,使用并查集处理结点的集合属性,初始时所有结点属于只包含其自身的孤立集合。依次遍历按照边权值递增排列的边时,若该边的两个顶点属于两个不同的集合,则合并这两个集合同时将该边的权值累加到答案中,直到遍历完所有的边。
该例不存在得不到最小生成树的情况,所以最后并没有对所有结点是否属于同一个集合进行判断,若可能出现不存在最小生成树的情况,则该步不能省略。

猜你喜欢

转载自blog.csdn.net/LYKXHTP/article/details/88600993