最小生成树求法 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

时间: 2024-10-06 13:26:33

最小生成树求法 Prim + Kruskal的相关文章

最小生成树之Prim Kruskal算法(转)

最小生成树 首先,生成树是建立在无向图中的,对于有向图,则没有生成树的概念,所以接下来讨论的图均默认为无向图.对于一个有n个点的图,最少需要n-1条边使得这n个点联通,由这n-1条边组成的子图则称为原图的生成树.一般来说,一个图的生成树并不是唯一的(除非原图本身就是一棵树). 现在考虑带权图G,即图的边带权,则最小生成树就是在G中权值和最小的一颗生成树,显然最小生成树也不是唯一的,但是其权值唯一.有很多应用需要用到最小生成树的概念,比较直观的一个应用就是:有n个村庄,现在要在这些村庄之间修一些路

最小生成树(prim&amp;kruskal)

最近都是图,为了防止几次记不住,先把自己理解的写下来,有问题继续改.先把算法过程记下来: prime算法:                                           原始的加权连通图——————D被选作起点,选与之相连的权值最小的边————选与D.A相连权值最小的边——————可选的有B(7).E(8).G(11)                   ————————————————————————————————————————————————————————————

『最小生成树』prim &amp; Kruskal

Kruskal和prim都是求最小生成树的方法,两种方法都是按照贪心来做的.但是Kruskal是从边的角度入手,prim则是从点的角度入手.prim运用类似Dijkstra的方法来求,Kruskal运用并查集来求. 在复杂度方面,两种算法各有所长. 在稀疏图中,枚举边的Kruskal效率更高,在稠密图中,枚举点的prim效率更高. Kruskal算法 按照边权从小到大排序,每次判断当前边两边的点是否连通,如果没有连通,就加入这条边. 连通性我们可以用并查集维护. 复杂度为\(O(m*log_m+

数据结构之 图论---最小生成树(prim + kruskal)

图结构练习——最小生成树 Time Limit: 1000MS Memory limit: 65536K 题目描述 有n个城市,其中有些城市之间可以修建公路,修建不同的公路费用是不同的.现在我们想知道,最少花多少钱修公路可以将所有的城市连在一起,使在任意一城市出发,可以到达其他任意的城市. 输入 输入包含多组数据,格式如下. 第一行包括两个整数n m,代表城市个数和可以修建的公路个数.(n<=100) 剩下m行每行3个正整数a b c,代表城市a 和城市b之间可以修建一条公路,代价为c. 输出

poj1861 最小生成树 prim &amp; kruskal

// poj1861 最小生成树 prim & kruskal // // 一个水题,为的只是回味一下模板,日后好有个照应不是 #include <cstdio> #include <algorithm> #include <cstring> #include <vector> #include <iostream> using namespace std; const int MAX_N = 1008; const int INF =

最小生成树(prim算法,Kruskal算法)c++实现

1.生成树的概念 连通图G的一个子图如果是一棵包含G的所有顶点的树,则该子图称为G的生成树. 生成树是连通图的极小连通子图.所谓极小是指:若在树中任意增加一条边,则将出现一个回路:若去掉一条边,将会使之变成非连通图. 生成树各边的权值总和称为生成树的权.权最小的生成树称为最小生成树. 2.最小生成树的性质用哲学的观点来说,每个事物都有自己特有的性质,那么图的最小生成树也是不例外的.按照生成树的定义,n 个顶点的连通网络的生成树有 n 个顶点.n-1 条边. 3.构造最小生成树,要解决以下两个问题

最小生成树之 prim算法和kruskal算法(以 hdu 1863为例)

最小生成树的性质 MST性质:设G = (V,E)是连通带权图,U是V的真子集.如果(u,v)∈E,且u∈U,v∈V-U,且在所有这样的边中, (u,v)的权c[u][v]最小,那么一定存在G的一棵最小生成树,(u,v)为其中一条边. 构造最小生成树,要解决以下两个问题: (1).尽可能选取权值小的边,但不能构成回路(也就是环). (2).选取n-1条恰当的边以连接网的n个顶点. Prim算法的思想: 设G = (V,E)是连通带权图,V = {1,2,-,n}.先任选一点(一般选第一个点),首

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

最小生成树概念: 一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边. 最小生成树可以用kruskal(克鲁斯卡尔)算法或prim(普里姆)算法求出.最小生成树其实是最小权重生成树的简称. prim: 概念:普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树.意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点,且其所有边的权值之和亦为最小. 实现过程: 图例 说明 不可选 可选 已选(Vn

最小生成树:prim算法和kruskal算法

一个连通图的生成树是图的极小连通子图.它包含图中的所有顶点,并且只含尽可能少的边.若砍去它的一条边,就会使生成树变成非连通图:若给它增加一条边,则会形成一条回路. 最小生成树有如下性质: 1.最小生成树非唯一,可能有多个最小生成树: 2.最小生成树的边的权值之和总唯一,而且是最小的: 3.最小生成树的边数为顶点数减1. 构造最小生成树可以有多种算法.其中多数算法利用了最小生成树的下列一种简称为MST的性质: 假设N=(V,{E})是一个连通网,U是顶点集V的一个非空子集.若(u, v)是一条具有