最小生成树模板 POJ——1258

最小生成树是一个比较简单数据结构,形成最小生成树的方式有两种。
最小生成树是有图生成树,保证树的每条边的权值之和最小的生成树就叫做最小生成树,这一类的题目起初比较基础,主要是熟悉模板,POJ 1258 也就是这样的题目,非常适合刚刚学习的人。
两种方法的思路分别是找边和找点,简单点说就是,找到全集中最小的边(或者是距离最近的点),将他们连接起来,最终形成的树就是最小生成树。

第一种方法,prim算法,时间复杂度较高不经常使用,是一种找点的方式
这种方式做题的时候最重要的就是要注意,必须注意是不是有一模一样的边,很容易出现类似错误,但是由于不经常使用,这里不多做阐述,以后回单独讲解。
列出模板:

int table[MAXN][MAXN];//用来记录每个点的始点到终点的距离
//例如table[1][2] = 3;表示的是1到2 的距离是3;
//这就是prim的比较差劲的地方,他必须每次都完全遍历寻找
int dis[MAXN];//边的最小距离
int used[MAXN];//标记是否使用过

void prim(){
    memset(used,0,sizeof(used));
    memset(dis,INF,sizeof(dis));
    dis[1]  =0;
    int sum=0;
    while(1){
        int smin = INF;
        int snt = -1;
        //寻找最短的还没有使用过的边
        for(int i=1;i<=n;i++){
            if(dis[i]<smin&&!used[i]){
                smin = dis[i];
                snt = i;
            }
        }
        //如果没找到跳出
        if(snt==-1)
            break;
         //找到最小的,也就是最终的边,所以就可以加进去了
        sum+=smin;
        used[snt] = 1;///标记已使用
        //用该边对于所有边进行松弛操作(也就是小则替换)
        for(int i=1;i<=n;i++){
            if(table[snt][i]!=INF&&dis[i]>table[snt][i]){
                dis[i] = table[snt][i];
            }
        }
    }
    printf("%d\n",sum);
}

这个题目就是 一个套入模板的板子题,下面是这个题的解答

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<math.h>
#include<stack>
#include<queue>
#include<algorithm>
#define MAXN 105
#define INF 0x3f3f3f3f
using namespace std;
int n;
int table[MAXN][MAXN];
int dis[MAXN];
int used[MAXN];

void prim(){
    memset(used,0,sizeof(used));
    memset(dis,INF,sizeof(dis));
    dis[1]  =0;
    int sum=0;
    while(1){
        int smin = INF;
        int snt = -1;
        for(int i=1;i<=n;i++){
            if(dis[i]<smin&&!used[i]){
                smin = dis[i];
                snt = i;
            }
        }
        if(snt==-1)
            break;
        sum+=smin;
        used[snt] = 1;
        for(int i=1;i<=n;i++){
            if(table[snt][i]!=INF&&dis[i]>table[snt][i]){
                dis[i] = table[snt][i];
            }
        }
    }
    printf("%d\n",sum);
}
int main()
{
    while(~scanf("%d",&n)){
        memset(table,INF,sizeof(table));
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                scanf("%d",&table[i][j]);
            }
        }
        prim();
    }


    return 0;
}

第二种方法 kruskal算法,是一种找边的思想
大体思路就是,通过寻找不是同一个树上的最短的边,将他们合在同一个树上形成最小生成树。在这个题目上应用,并不是太明显,这个题数据量太小
详细的就看代码注释吧!主要是应用了并查集,https://blog.csdn.net/weixin_40488730/article/details/81586804
如果没有接触过并查集,可以看一下,再回来看下面的代码


#include <cstdio>
#include <algorithm>
#include<cstring>
#include<iostream>
using namespace std;
const int maxn = 105;
struct edge{
    int u, v, w;
    bool operator < (const edge & p) const {
        return w < p.w;
    }
} edges[maxn*maxn];
int p[maxn];
int n;

//并查集的查找函数,带有压缩路径
int _find(int x){
    if(x!=p[x])
    {
        p[x] = _find(p[x]);
    }
    return p[x];

}

int kruskal(){
    int ans = 0;
    int t=n*n, u, v;
    for (int i=n; i<t; ++i){
        u = _find(edges[i].u);
        v = _find(edges[i].v);
        if (u != v){
            ans += edges[i].w;
            p[u] = v;
        }
    }
    return ans;
}

int main(){
    while (scanf("%d", &n)!=EOF){
        for (int i=0; i<n; ++i)
            p[i] = i;
        int w, cnt=0;
        for (int i=0; i<n; ++i)
            for (int j=0; j<n; ++j){
                scanf("%d", &w);
                edges[cnt].u = i;
                edges[cnt].v = j;
                edges[cnt++].w = w;
            }
        sort(edges, edges+n*n);
        printf("%d\n", kruskal());
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_40488730/article/details/81977168