Prim算法的3个版本

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Prim算法的3个版本相关的知识,希望对你有一定的参考价值。

Prim:以贪心的思想求得最小生成树: 把已建成的树看成一个结点, 然后用贪心的方法每次添加距离最短的点。

以Poj1258为例:http://poj.org/problem?id=1258

 

1.朴素版本:邻接矩阵, 无任何优化, O(n^2)

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstdlib>
 4 #include <cstring>
 5 #include <algorithm>
 6 #include <cmath>
 7 using namespace std;
 8 
 9 const int maxn = 100 + 2;
10 int map[maxn][maxn];
11 bool vis[maxn];
12 int dist[maxn];
13 int n;
14 
15 int Prim(int x)
16 {
17     int ret = 0, p = 0;
18     vis[x] = true;
19     dist[x] = 0;
20     
21     for(int i = 1; i < n; i++)
22     {
23         for(int r = 1; r <= n; r++) 
24             if(map[x][r])
25                 dist[r] = min(dist[r], map[x][r]);
26         
27         for(int r = 1; r <= n; r++)
28             if(!vis[r])
29                 p = p ? (dist[p] < dist[r] ? p : r) : r;
30 
31         ret += dist[p];
32         x = p, p = 0;
33         vis[x] = true;
34     }
35     return ret;
36 }
37 
38 int main()
39 {
40     //freopen("in.txt", "r", stdin);
41     
42     while(~scanf("%d", &n))
43     {
44         for(int i = 1; i <= n; i++)
45             for(int r = 1; r <= n; r++)
46                 scanf("%d", &map[i][r]);
47                 
48         memset(vis, false, sizeof(vis));
49         memset(dist, 0x7f, sizeof(dist));
50  
51         printf("%d\n", Prim(1));    
52     }
53 } 

也没什么可解释的.

 

 

2.临界矩阵, 优先队列优化(堆同), O(nlogn)

 1 #include <iostream>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <cstdio>
 5 #include <algorithm>
 6 #include <cmath>
 7 #include <queue>
 8 using namespace std;
 9 
10 const int maxn = 100 + 2;
11 priority_queue< pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > q;
12 int map[maxn][maxn];
13 bool vis[maxn];
14 int dist[maxn];
15 int n;
16 
17 int Prim(int x)
18 {    
19     memset(vis, false, sizeof(vis));
20     memset(dist, 0x7f, sizeof(dist));
21     int ret = 0;
22     dist[x] = 0;
23     
24     while(!q.empty()) q.pop();
25     
26     for(int i = 1; i < n; i++)
27     {
28         vis[x] = true;
29         
30         for(int r = 1; r <= n; r++)
31             if(dist[r] > map[x][r] && map[x][r] && !vis[r])
32                 dist[r] = map[x][r], q.push(make_pair(dist[r], r));
33             
34         while(!q.empty() && vis[q.top().second]) 
35             q.pop();    
36         
37         x = q.top().second;
38         ret += dist[x];
39     }
40     return ret;
41 }
42 
43 int main()
44 {
45 //    freopen("in.txt", "r", stdin);
46     
47     while(~scanf("%d", &n))
48     {
49         for(int i = 1; i <= n; i++)
50             for(int r = 1; r <= n; r++)
51                 scanf("%d", &map[i][r]);
52             
53         printf("%d\n", Prim(1));
54     }
55     
56     
57 } 

<1>.pair<typename, pytename> p:是一种结构体,只有两个元素,<, >两个typename 分别为两个元素的类型

通过 p.first, p.second 分别访问第一个元素和第二个元素。

<2>.memset(name, value, sizeof), 属于 string.h 是字符数组的填充函数, 因为字符char类型为1字节,所以填充int数组时也是按单字节填充, 初始化时若每个字节的值过大, 会出现负数(整数存储原理, 补码。)

<3> priority_queue< pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > q;为声明小根堆(个人感觉优先队列和堆区分不用很明显, 因为内部存储结构已经不重要), 相邻的两个相同左右尖括号要用空格隔开, 否则会组成左移右移运算符, 此程序中时对pair进行排序, 在排序时以第一个元素为关键字, 所以pair.first应该存dist, 而不是点的编号。

<4>.补充一下。priority_queue< pair<int, int> > q; 为声明大根堆

 

3.邻接表, 优先队列

 1 #include <iostream>
 2 #include <cstdlib>
 3 #include <cstdio>
 4 #include <cstring>
 5 #include <queue>
 6 using namespace std;
 7 
 8 const int maxn = 10000 + 2, maxm = 100000 + 2;
 9 #define pii pair<int, int>
10 #define fi first
11 #define se second
12 #define MP make_pair
13 
14 priority_queue<pii, vector<pii >, greater<pii > > heap;
15 int n, m, dist[maxn], s, t;
16 bool vis[maxn];
17 
18 struct arc
19 {
20     int e, v;
21     arc *next;
22     arc(){}
23     arc(int e, int v, arc *next) : e(e), v(v), next(next){}
24     void *operator new(unsigned, void *p) {return p;}
25 }*head[maxn], mem[maxm * 2], *sp = mem;
26 
27 inline void addarc(int x, int y, int v)
28 {
29     head[x] = new(sp++) arc(y, v, head[x]);
30     head[y] = new(sp++) arc(x, v, head[y]);
31 }
32 
33 int Prim(int x)
34 {
35     memset(dist, 0x7f, sizeof(dist)); 
36     dist[x] = 0;
37     int ans = 0;
38     for (int i = 1; i < n; i++)
39     {
40         vis[x] = true;
41         for (arc *p = head[x]; p; p = p -> next)
42             if (dist[p -> e] > p -> v)
43                 dist[p -> e] = p -> v, heap.push(MP(dist[p -> e], p -> e));
44         
45         while (!heap.empty() && vis[heap.top().se]) heap.pop();
46         
47         x = heap.top().se;
48         ans += dist[x];
49     }
50     return ans;
51 }
52 
53 int main()
54 {
55     freopen("Prim.in", "r", stdin);
56     freopen("Prim.out", "w", stdout);
57 
58     scanf("%d%d", &n, &m);
59     int a, b, c;
60     for (int i = 1; i <= m; i++) 
61         scanf("%d%d%d", &a, &b, &c), addarc(a, b, c);
62     
63     printf("%d\n", Prim(1));
64 
65     return fclose(stdin), fclose(stdout), 0;    
66 }

1.前向星存储图,一种牺牲空间获取时间的方法,在算法竞赛中的试用比较普遍的做法。

2.没什么好解释的了...

 

以上是关于Prim算法的3个版本的主要内容,如果未能解决你的问题,请参考以下文章

最小生成树详解 prim+ kruskal代码模板

什么是Prim算法?

数据结构(prim算法)

为什么Prim算法不适用于带权有向图

prim算法和kruskal算法--解最小生成树问题

(学习1)最小生成树-Prim算法与Kruskal算法