最小生成树MST-Kruskal算法

时间:2022-04-10 09:53:03

RT,题目链接http://zju.acmclub.com/index.php?app=problem_title&id=1&problem_id=2336

需要注意的问题有:

1.可以使用Kruskal算法,在判断回路时使用并查集结构;

2.并查集使用树的双亲指针数组作为存储结构,大小为树中点的数目;

初始化时:初始每一个点为一个单独的联通分量,S[i]=-1 for 1<=i<=|V|;

Find(S,x):返回单元素x所在的子集合名字(用树根来代表),这里如果两个点所返回的子集合名字相同,说明这两个点同属于一个连通分量;这种情况也说明,如果把当前边再加入这个连通分量中,就会产生回路;

Union(S,root1,root2):把以x为根的子集合与以y为根的子集合合并到一起;即对应把两个连通分量合并为一个,root1和root2可以通过Find函数找到根;

3.需要额外判断给定的数据是否能使图连通,方法是执行Kruskal算法后,数一数S[]数组中-1的个数,它的个数cnt即代表了算法结束后连通分量的个数,如果为1,说明有一个MST;否则若图不连通,树也必定不连通,故cnt一定大于1

4.边集用结构体数组来存,可以开的大一点,我刚开始就是开小了返回段错误;

5.排序可以用高效的库函数qsort,自己写一个比较函数MyCmp实现结构体数组按特定值排序的功能,刚开始我是自己用的起泡排序+用邻接矩阵来存树,结果果断时间超限。。。

cpp代码:

#include<iostream>
#include<cstdlib>
#define VERTEX_MAX 101
#define EDGE_MAX 5000
using namespace std;
//并查集类,根据并查集就可以判断图是否是连通的
class UFSets
{
    private:
        int S[VERTEX_MAX];
    public:
        UFSets(int size_vertex){
            for (int i=1;i<=size_vertex;i++)S[i]=-1;
        }
        ~UFSets(){}//析构
        void print(int size_vertex){//输出当前并查集数组测试一下
            for (int i=1;i<=size_vertex;i++)cout<<S[i]<<" ";
            cout<<endl;
        }
        int Find(int x){//在S中查找包含x的树的根
            while(S[x]>=0)x=S[x];
            return x;
        }
        void Union(int root1,int root2){
            S[root2]=root1;
        }
        int isConnect(int size_vertex){
            int cnt=0;//记录数组中-1的个数,有几个-1说明有几个连通分量
            for (int i=1;i<=size_vertex;i++){
                if (S[i]==-1)cnt++;
            }
            if (cnt==1)return 1;
            else return 0;
        }
};
struct EDGES{
    int x;
    int y;
    int w;
};
int MyCmp(const void*a,const void*b)
{
    return (*(EDGES*)a).w-(*(EDGES*)b).w;
}
int main(){
    int n,m,a,b,l,k,pos;
    int mk[VERTEX_MAX];
    struct EDGES edge[EDGE_MAX];
    while(cin>>n>>m){
        if (n==0)break;
        pos=0;
        while(n--){
            cin>>a>>b>>l;
            edge[pos].x=a;
            edge[pos].y=b;
            edge[pos].w=l;
            pos++;
        }
        qsort(edge,pos,sizeof(edge[0]),MyCmp);
        int cost=0;//累积最小生成树的代价
        UFSets sets(m);//初始化并查集
        //开始生成MST
        for(k=0;k<pos;k++){
            //每次取权重最小的边
            if(sets.Find(edge[k].x)!=sets.Find(edge[k].y)){//如果不构成回路,就增加边
                //cout<<"i="<<edge[k].x<<" j="<<edge[k].y<<" w="<<edge[k].w<<endl;
                cost+=edge[k].w;
                sets.Union(sets.Find(edge[k].x),sets.Find(edge[k].y));
            }
        }
        if(sets.isConnect(m))cout<<cost<<endl;
        else cout<<"Error!"<<endl;

    }
}