普利姆(Prime)算法

来源:互联网 发布:淘宝上复制链接在哪里 编辑:程序博客网 时间:2024/05/16 12:17

参考:http://blog.csdn.net/niushuai666/article/details/6689295

普利姆(Prime)算法(只与顶点相关)

 

算法描述:

普利姆算法求最小生成树时候,和边数无关,只和定点的数量相关,所以适合求稠密网的最小生成树,时间复杂度为O(n*n)。

算法过程:

1.将一个图的顶点分为两部分,一部分是最小生成树中的结点(A集合),另一部分是未处理的结点(B集合)。

2.首先选择一个结点,将这个结点加入A中,然后,对集合A中的顶点遍历,找出A中顶点关联的边权值最小的那个(设为v),将此顶点从B中删除,加入集合A中。

3.递归重复步骤2,直到B集合中的结点为空,结束此过程。

4.A集合中的结点就是由Prime算法得到的最小生成树的结点,依照步骤2的结点连接这些顶点,得到的就是这个图的最小生成树。


算法实现具体过程:

1.将第一个点放入最小生成树的集合中(标记visit[i]=1意思就是最小生成树集合)。

2.从第二个点开始,初始化lowcost[i]为跟1点相连(仅仅相连)的边的权值(lowcost[i]不是这个点的最小权值!在以后会逐步更新)。

3.找最小权值的边。

从第二点开始遍历,如果不是最小生成树的集合的点,则找出从2到n的最小权值(lowcost[j])。

4.将找出来的最小权值的边的顶点加入最小生成树的集合中(标记visit[i] = 1),权值想加。

5.更新lowcost[j]集合。

假设第一次:lowcost[2]代表与1相连的点的权值,现在加入了k点。则比较k点与2点的边map[k][2]和lowcost[2]的大小,若lowcost[2]大,则lowcost[2] = map[k][2]。(关键步骤:实质就是每在最小生成树集合中加入一个点就需要把这个点与集合外的点比较,不断的寻找两个集合之间最小的边)

6.循环上述步骤,指导将全部顶点加入到最小生成树集合为止。

具体步骤看下图:


首先构造无向图

其次构造出邻接矩阵




第一步:从①开始,①进集合,用与集合外所有顶点能构成的边中找最小权值的一条边
①——②权6
①——③权1 -> 取①——③边
①——④权5

 

第二步:③进集合,①,③与②,④,⑤,⑥构成的最小边为
①——④权5
③——⑥权4 -> 取③——⑥边

第三步:⑥进集合,①,③,⑥与②,④,⑤构成的各最小边
①——②权6
③——②权5
⑥——④权2 -> 取⑥——④边

第四步:④进集合,①,③,⑥,④与②,⑤构成的各最小边
①——②权6
③——②权5 -> 取③——②边
⑥——⑤权6

第四步:②进集合,①,③,⑥,②,④与⑤构成的各最小边
②——⑤权3 -> 取②——⑤边



算法实现过程就是:

1.先随便选取一个点加入集合S(不影响后续结果)

2.初始化这个点到各点的距离

上边的都是准备工作,下面算法开始:

3.首先遍历未加入集合S的点集,找出这些点的最短边权;

4.然后再通过新加入的点(上一步求出的具有最短边权的点)与未加入的点的距离联系,得到新加入的点到外界的最短距离以此更新未加入点的最短边权;(说白了这些过程就是为了更新未加入的点的最小边权以此达到每加入一个点到S集合都能保证他是最小值)

下面是具体过程图解:


代码实现:

void prime(int cur){                       //随便选取一个初始点memset(vis,0,sizeof(vis));for(int i = 1;i<=n;i++){dist[i] = a[cur][i];      //给其余个点的最短边权赋初值(dist数组是保存最短边权的,但此时不是最短,下面会动态更新)}vis[cur] = 1;                    // 将初始点加入S集合int index;               for(int i=1;i<=n;i++){                            //直到把所有点全部加入S集合,才算结束int mincost = INF;for(int j=1;j<=n;j++){                      //遍历各未加入到S集合的点,得到最短边权以及相应的点if(!vis[j] && mincost > dist[j]){mincost = dist[j];index = j;}}if(mincost == INF) break; vis[index] = 1;                //将相应点加入S集合ans += mincost;               //累加边权和for(int j=1;j<=n;j++){                        // 更新dist[]数组,保证最小边权if(!vis[j] && dist[j] > a[index][j]){     //如果j这个点原来储存的最短边权大于与index点相连的权,则更新dist[j] = a[index][j];}}}}

例题hihocoder1097:

#1097 : 最小生成树一·Prim算法

时间限制:10000ms
单点时限:1000ms
内存限制:256MB

描述

最近,小Hi很喜欢玩的一款游戏模拟城市开放出了新Mod,在这个Mod中,玩家可以拥有不止一个城市了!

但是,问题也接踵而来——小Hi现在手上拥有N座城市,且已知这N座城市中任意两座城市之间建造道路所需要的费用,小Hi希望知道,最少花费多少就可以使得任意两座城市都可以通过所建造的道路互相到达(假设有A、B、C三座城市,只需要在AB之间和BC之间建造道路,那么AC之间也是可以通过这两条道路连通的)。

提示:不知道为什么Prim算法和Dijstra算法很像呢Σ(っ °Д °;)っ 。

输入

每个测试点(输入文件)有且仅有一组测试数据。

在一组测试数据中:

第1行为1个整数N,表示小Hi拥有的城市数量。

接下来的N行,为一个N*N的矩阵A,描述任意两座城市之间建造道路所需要的费用,其中第i行第j个数为Aij,表示第i座城市和第j座城市之间建造道路所需要的费用。

对于100%的数据,满足N<=10^3,对于任意i,满足Aii=0,对于任意i, j满足Aij=Aji, 0<Aij<10^4.

输出

对于每组测试数据,输出1个整数Ans,表示为了使任意两座城市都可以通过所建造的道路互相到达至少需要的建造费用。

样例输入
50 1005 6963 392 1182 1005 0 1599 4213 1451 6963 1599 0 9780 2789 392 4213 9780 0 5236 1182 1451 2789 5236 0 
样例输出
4178
#include <bits/stdc++.h>using namespace std;const int INF = 1234567;const int AX = 1e3+666;int n;int a[AX][AX];int dist[AX];int vis[AX];int ans;void prime(int cur){memset(vis,0,sizeof(vis));for(int i = 1;i<=n;i++){dist[i] = a[cur][i];}vis[cur] = 1;int index;for(int i=1;i<=n;i++){int mincost = INF;for(int j=1;j<=n;j++){if(!vis[j] && mincost > dist[j]){mincost = dist[j];index = j;}}if(mincost == INF) break;vis[index] = 1;ans += mincost;for(int j=1;j<=n;j++){if(!vis[j] && dist[j] > a[index][j]){dist[j] = a[index][j];}}}}int main(){ans = 0;scanf("%d",&n);for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){scanf("%d",&a[i][j]);}} prime(2);   //随意选printf("%d\n",ans);return 0;}



原创粉丝点击