最小生成树求法 Prim + Kruskal
prim算法的思路 和dijkstra是一样的
每次选取一个最近的点 然后去向新的节点扩张 注意这里的扩张 不再是 以前求最短路时候的到新的节点的最短距离
而是因为要生成一棵树 所以是要连一根最短的连枝 所以关键部分修改一下
dist[u] = min(dist[u], e.cost) --->>e是连接 v 和 u的边
同样地 普同写法O(v^2) 用队列优化后O(E*logV)
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #include <queue> 5 #define MAXV 256 6 #define MAXE 256 7 #define INF 0x3f3f3f3f 8 9 using namespace std; 10 typedef pair<int,int> P; 11 int V, E; 12 //和dijkstra完全一样 13 //书上写法 O(V^2) 14 int graph[MAXV][MAXV]; 15 int prim() 16 { 17 int dist[MAXV]; 18 int res = 0; 19 bool use[MAXV]; 20 fill(dist, dist+MAXV, INF); 21 fill(use, use+MAXV, 0); 22 dist[1] = 0;//假定1为源点 23 while (true) 24 { 25 int v = -1; 26 for (int i = 1; i <= V; i++) 27 { 28 if (!use[i] && (v == -1 || dist[i] < dist[v])) v = i;//查找离原点最近的点 29 } 30 if (v == -1) break; 31 use[v] = true; 32 res += dist[v]; 33 cout << dist[v] << endl; //打印树枝的情况 34 for (int i = 1; i <= V; i++) 35 { 36 dist[i] = min(dist[i], graph[v][i]);//这里是唯一的区别 --->>.但其实 这个dist中的值 最后是有问题的 这样最后dist保存的 是离它最近的一个节点的边的值 37 //第33行打印的结果之所以正确是因为 按着离原点最近的顺序 向外扩张 在dist改变之前已经打印了 但是打印之后它的值是可能会发生改变的 38 /* 39 3).重复下列操作,直到Vnew = V: 40 41 a.在集合E中选取权值最小的边<u, v>,其中u为集合Vnew中的元素, 42 而v不在Vnew集合当中,并且v∈V(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一); 43 44 b.将v加入集合Vnew中,将<u, v>边加入集合Enew中; 45 */ 46 } 47 } 48 return res; 49 } 50 51 //使用堆维护 再使用prim O(E*logV) 52 struct Edge 53 { 54 int to, cost, next; 55 Edge() {} 56 Edge(int to, int cost, int next) : to(to), cost(cost), next(next) {} 57 }edge[MAXE]; 58 int num = 0; 59 int head[MAXV]; 60 void Add(int from, int to, int cost) 61 { 62 edge[num] = Edge(to, cost, head[from]); 63 head[from] = num++; 64 } 65 66 int prim2() 67 { 68 int dist[MAXV], res = 0; 69 bool use[MAXV]; 70 fill(use, use+MAXV, false); 71 fill(dist, dist+MAXV, INF); 72 priority_queue<P, vector<P>, greater<P> > que; 73 dist[1] = 0;//假定1为源点 74 que.push(P(dist[1], 1));//first -->距离 second -->编号 75 while(!que.empty()) 76 { 77 P p = que.top(); 78 que.pop(); 79 if (!use[p.second])//这个也是那个问题 先前点(作为现在去扩张的点 如果被打印过 就不再去打印它了 总之算法思路就是 从原点开始 让最近的一个点去扩张) 80 { 81 res += dist[p.second]; 82 cout << dist[p.second] << endl; 83 } 84 use[p.second] = true; 85 if (dist[p.second] < p.first) 86 { 87 continue; 88 } 89 int t = head[p.second]; 90 while (t != -1) 91 { 92 Edge e = edge[t]; 93 if (!use[e.to] && dist[e.to] > e.cost) 94 { 95 dist[e.to] = e.cost; 96 que.push(P(e.cost, e.to)); 97 } 98 t = e.next; 99 } 100 } 101 return res; 102 } 103 104 105 int main() 106 { 107 freopen("in.txt", "r", stdin); 108 scanf("%d%d", &V, &E); 109 memset(edge, -1, sizeof(edge)); 110 memset(head, -1, sizeof(head)); 111 for(int i = 1; i <= V; i++) 112 for (int j = 1; j <= V; j++) graph[i][j] = INF; 113 for (int i = 0; i < E; i++) 114 { 115 int from, to, cost; 116 scanf("%d%d%d", &from, &to, &cost); 117 graph[from][to] = cost; 118 graph[to][from] = cost; 119 Add(from, to, cost); 120 Add(to, from, cost); 121 } 122 int ans = prim2(); 123 cout << ans << endl; 124 }
Kruskal -->> O(E*logV) 思路就更加的简单
将所有的边 排序
贪心地从小到大取 如果连接进一个新的点 就加入树枝的集合中 最终 得到的边的集合就是最小生成树
这里判断是否是新的节点 就是判断连通性 那么就使用并查集非常容易
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #include <algorithm> 5 #define MAXV 256 6 #define MAXE 256 7 #define INF 0x3f3f3f3f 8 using namespace std; 9 10 int E, V; 11 //Kruskal 思路: 按边的角度出发 将边从小到大排序 如果 from to 不再一个连通块中 就可以取这一条边(不会产生环) 12 struct Edge 13 { 14 int from, to, cost; 15 Edge(){} 16 Edge(int from, int to, int cost) : from(from), to(to), cost(cost) {} 17 }edge[MAXE]; 18 19 int par[MAXV]; 20 int find(int x) 21 { 22 if(x == par[x]) return x; 23 else return par[x] = find(par[x]); 24 } 25 void unite(int x, int y) 26 { 27 int px = find(x), py = find(y); 28 if (px == py) return ; 29 else par[py] = px; 30 } 31 bool same(int x, int y) 32 { 33 int px = find(x), py = find(y); 34 return px == py; 35 } 36 37 bool cmp(Edge e1, Edge e2) 38 { 39 return e1.cost < e2.cost; 40 } 41 42 //O(E*logV) -->>对于并查集的操作是logN 43 int Kruskal() 44 { 45 int res = 0; 46 for (int i = 1; i <= V; i++) par[i] = i; 47 sort(edge, edge+E, cmp); 48 for (int i = 0; i < E; i++) 49 { 50 Edge e = edge[i]; 51 if (!same(e.from, e.to)) 52 { 53 cout << e.from << " " << e.to << " : " << e.cost << endl; 54 res += e.cost; 55 unite(e.from, e.to); 56 } 57 } 58 return res; 59 } 60 61 int main() 62 { 63 freopen("in.txt", "r", stdin); 64 scanf("%d%d", &V, &E); 65 for (int i = 0; i < E; i++) 66 { 67 int from, to, cost; 68 scanf("%d%d%d", &from, &to, &cost); 69 edge[i] = Edge(from, to, cost); 70 } 71 int ans = Kruskal(); 72 cout << ans << endl; 73 return 0; 74 }
转一篇写的很详细的博客
http://www.cnblogs.com/biyeymyhjob/archive/2012/07/30/2615542.html