Prim求最小生成树

题目传送门

\(Prim\)也挺简单的。我们来看一下具体思路

1.初始化\(dis\)距离数组为极大值。

2.迭代所有结点。

3.定义点\(t\),用\(t\)来找到集合外距离最小的点,每次如果这个点不在集合里并且\(t\)还是初始值,或者\(dist_t>dist_j\),就更新\(t\)的点。

4.特判,更新最小生成树的权值,然后用点\(t\)更新,其他点和集合的距离,将点\(t\)放入最小生成树。

是不是很像\(dij\)算法(迪杰斯特拉)?没错差距就只有一处,那就是一个是更新到起点的距离,一个是更新的到集合的距离,那什么是到集合的距离呢?

很简单,假设有一个点到集合有\(x\)条边,那其中最短的边就是到集合的距离。

时间复杂度\(O(n^2)\),相较于\(Kruskal\)慢了很多,但毕竟他还能存哪些节点是最小生成树的,还可以吧。

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 510, INF = 0x3f3f3f3f;
int n,m;

int g[N][N],dis[N];
bool st[N];//表示点是否在集合当中


int prim()
{
    memset(dis,0x3f,sizeof(dis));
    
    int res = 0;//最小生成树的边权值和
    for (int i = 0; i < n; i ++ )
    {
        int t = -1;
        for (int j = 1; j <= n; j ++ )//找到集合外距离最小的点
        {
            if(!st[j] && (t == -1 || dis[t] > dis[j]))
                t = j;
        }
        if(i && dis[t] == INF) return INF;
        if(i) res += dis[t];//如果不是第一个点,那么就把这个点用那条边和集合连起来
        
        for (int j = 1; j <= n; j ++ )
            dis[j] = min(dis[j],g[t][j]);
        

        st[t] = true;
    }
    return res;
}

int main()
{
    scanf("%d%d", &n, &m);
    memset(g,0x3f,sizeof(g));
    while (m -- )
    {
        int a,b,c;
        scanf("%d%d%d", &a, &b, &c);
        g[a][b] = g[b][a] = min(g[a][b],c);//无向图
    }
    int t = prim();
    if(t == INF) puts("impossible");
    else printf("%d\n",t);
    return 0;
}

更好的视觉体验

posted @ 2022-07-19 13:30  ljfyyds  阅读(46)  评论(0)    收藏  举报