Prime 算法

该算法的详细介绍可参考《算法进阶指南》,其中的描述较为容易理解,以下关于该算法的介绍也源自此书。

- Prime 算法 -

最小生成树:

给定一张边带权的无向图 G = ( V, E ), n=| V |, m=| E |。由 V 中全部 n 个顶点和 E 中 n-1 条边构成的无向连通子图被称为 G 的一棵生成树。边的权值之和最小的生成树被称为无向图 G 的最小生成树 ( Minimum Spanning Tree, MST ) 。任意一棵最小生成树一定包含无向图中权值最小的边。

Prime 算法:

Prime 算法总是维护最小生成树的一部分。最初,Prime 算法仅确定1号节点属于最小生成树。在任意时刻,设已确定属于最小生成树的节点集合为 T ,剩余节点集合和 S 。Prime 算法找到 min{z} (z是两个端点分别属于集合 S,T 的权值最小的边),然后把点 x 从集合 S 中删除,加入到集合 T ,并把z累加到答案中。
也就是维护数组 w :若 x 属于 S 则 w[x] 表示节点 x 与集合 T 中的节点之间权值最小的边的权值。若 x 属于 T ,则 w[x] 就等于 x 被加入 T 时选出的最小边的权值,这步操作可类比 Dijkstra 算法。最后,最小生成树的权值总和就是除了第一个加入集合 T 的点的所有n-1个节点的 w[x] 值之和。

复杂度:

时间复杂度:O(n^2) 可以用二叉堆优化到 O(m log n) 。但用二叉堆优化不如直接使用 Kruskal 算法来得方便。因此,Prim 算法主要用于稠密图,尤其是完全图的最小生成树的求解。

算法过程:

1、构建一个 n 阶的邻接矩阵mp(n 和 m 由题目给定)。( mp 、vis、dis、w 数组都是全局定义)

memset(mp, INF, sizeof(mp));
for(int i=1; i<=n; i++) mp[i][i]=0;
for(int i=1; i<=m; i++) {
    scanf("%d %d %d", &x, &y, &d);
    mp[x][y]=d;
}

2、求最小生成树。

void prim() {
    memset(vis, 0, sizeof(vis));//vis=0表示未访问
    vis[1]=1;
    for(int i=1; i<=n; i++) {//初始化
        dis[i]=1;//初始化点i取得最短路径时指向1,即边1-i
        w[i]=mp[1][i];//初始化所有点的最短路径为1到顶点的距离,即权值
    }
    int ans=0, mind, x;
    for(int i=1; i<n; i++) {//索引次数为n-1(每次找出一条最短路径)
        mind=INF, x=i;
        for(int j=1; j<=n; j++) {
            if(!vis[j] && w[j]<mind) {
                mind=w[j];
                x=j;
            }
        }
        vis[x]=1;
        ans+=mind;
        for(int j=1; j<=n; j++) {
            if(!vis[j] && mp[x][j]<w[j]) {
                dis[j]=x;
                w[j]=mp[x][j];//更新权值
            }
        }
    }
    printf("%d\n", ans);//求最小生成树
    return ;
}

例题:

- HDU 1102 -

Constructing Roads

Time Limit: 2000/1000 MS (Java/Others) | Memory Limit: 65536/32768 K (Java/Others)

题意:

给定一个整数 n ,表示村庄数(从1~n编号),接下来 n 行,每行 n 个整数,第 i 行第 j 列的数表示村庄 i 和 村庄 j 之间的距离,然后给定一个整数 q ,接下来 q 行每行有两个数a,b,表示村庄 a、b 之间的路已连通,最后求使得所有村庄互相连通需要修的最短的路。

数据范围:

N (3 <= N <= 100),村庄之间的距离在 [ 1, 1000 ] 范围内,Q (0 <= Q <= N * (N + 1) / 2), (1 <= a < b <= N)

解题思路:

Prime 算法 or Kruskal 算法 以下选择用Prime 算法实现
用 Kruskal 算法实现的代码链接–>- Kruskal 算法 -

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#include <map>
using namespace std;
#define INF 0x3f3f3f

typedef long long ll;
const int N=105;
int mp[N][N], vis[N], dis[N], w[N];

void init() {
    memset(mp, INF, sizeof(mp));
    memset(vis, 0, sizeof(vis));//vis=0表示未访问
    return ;
}

void prim(int n) {
    memset(mp, INF, sizeof(mp));
    memset(vis, 0, sizeof(vis));//vis=0表示未访问
    vis[1]=1;
    for(int i=1; i<=n; i++)
        w[i]=mp[1][i];//初始化所有点的最短路径(即权值)为1到顶点的距离
    int ans=0, mind, x;
    for(int i=1; i<n; i++) {//索引次数为n-1(每次找出一条最短路径)
        mind=INF, x=i;
        for(int j=1; j<=n; j++) {
            if(!vis[j] && w[j]<mind) {
                mind=w[j];
                x=j;
            }
        }
        vis[x]=1;
        ans+=mind;
        for(int j=1; j<=n; j++) {
            if(!vis[j] && mp[x][j]<w[j]) 
                w[j]=mp[x][j];//更新最短路径
        }
    }
    printf("%d\n", ans);//求得使得所有村庄互相连通的最短路径和
    return ;
}

int main() {
    int n, q, d;
    while(scanf("%d", &n)!=EOF) {//居然是多组输入!!!
        init();
        for(int i=1; i<=n; i++) {
            for(int j=1; j<=n; j++) {
                scanf("%d", &d);
                mp[i][j]=d;
            }
        }
        scanf("%d", &q);
        int a, b;
        for(int i=0; i<q; i++) {
            scanf("%d %d", &a, &b);
            mp[a][b]=mp[b][a]=0;//在这里只要把已修建了路的村庄间的距离记为0就好了
        }
        prim(n);
    }
    return 0;
}
  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值