图论一直是自己算法中最最最柔弱的部分,主要是,当年数据结构的课程,后面就去打酱油了,后来时间又都花在了电赛上,平时用的相关部又少,这一部分就更弱了,总是懒得捡起来,但是现在可是没退路了,开始好好复习这一部分。
Prim算法是求解无向图最小生成树的经典算法,和Dijkstra算法类似,但是Prim算法每次获取一个新的顶点后,都有一个聚合的过程,而Dijkstra没有,并且Dijkstra的对象是有向图的单源最短路径,所以这两个算法起始本质上是不同的,然而他们的思想是一致的:贪心
刚好借着hihocoder的这个题实现一下这个算法,刚开始的时候拿STL的set和vector做的,但是由于没有用辅助数组,也没有用到聚合的思想,于是傻傻的写了个O(n3)的算法,直接超时了,用例测时,3300多ms,后来醒悟过来,明明记得Prim是O(n2)的算法,于是果断丢掉set和vector,重新试实现了下,添加辅助数组low。
总结:
Prim算法时间复杂度O(n2),适合于边比较密集的Graph。另外一个算法Kruskal的算法O(eloge),适合于边比较稀疏的情况。
Impl:
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <climits> 5 6 using namespace std; 7 8 int w[1010][10010]; 9 int low[10010]; 10 bool visited[10010]; 11 int n; 12 13 int prim() 14 { 15 int pos,min,res = 0; 16 int left = n; 17 18 //选取任意点作为开始节点,初始化辅助数组 19 visited[0] = true; pos = 0; 20 for (int i = 0; i < n; ++i){ 21 if (i != pos) low[i] = w[pos][i]; 22 } 23 24 for (int i = 1; i < n; ++i) 25 { 26 min = INT_MAX; 27 for (int j = 0; j < n; ++j) //找出收缩后的最优节点 28 if (!visited[j] && min>low[j]) 29 min = low[j], pos = j; 30 31 res += min; 32 visited[pos] = true; 33 34 for (int j = 0; j < n; ++j) //一个收缩的过程,刷新辅助数组 35 if (!visited[j] && low[j]>w[pos][j]) 36 low[j] = w[pos][j]; 37 } 38 39 return res; 40 } 41 42 43 int main() 44 { 45 scanf("%d", &n); 46 for (int i = 0; i < n; ++i) 47 for (int j = 0; j < n; ++j) 48 scanf("%d", &w[i][j]); 49 memset(visited, 0, sizeof(visited)); 50 51 printf("%d\n", prim()); 52 53 return 0; 54 }
时间: 2024-10-28 14:30:27