最小生成树算法

合集下载

prim算法求最小生成树c代码

prim算法求最小生成树c代码

一、概述在图论中,最小生成树是指在一个连通图中生成一棵包含图中所有顶点且边权值之和最小的树。

prim算法是一种常用的求取最小生成树的算法之一,其基本思想是从一个起始顶点开始,逐步选择与当前树相邻的并且权值最小的边,直到包含了图中所有的顶点为止。

本文将介绍prim算法的原理以及给出相应的C代码实现。

二、prim算法原理1. 初始化选择任意一个顶点作为起始顶点,并将其标记为已访问。

设置一个集合V来存放已经访问的顶点,初始化为空集合。

2. 重复以下步骤直到V包含了所有顶点:(1)遍历集合V中的所有顶点,找到与之相邻且未被访问过的顶点中边权值最小的顶点,将其加入集合V中。

(2)将找到的顶点与其相邻的边加入最小生成树中。

3. 输出最小生成树当集合V包含了所有顶点之后,即可输出最小生成树。

三、prim算法C代码实现以下是prim算法的C代码实现:#include <stdio.h>#include <limits.h>#define V 5 // 顶点个数int minKey(int key[], bool mstSet[]) {int min = INT_MAX, min_index;for (int v = 0; v < V; v++) {if (!mstSet[v] key[v] < min) {min = key[v], min_index = v;}}return min_index;}void printMST(int parent[], int graph[V][V]) {printf("Edge \tWeight\n");for (int i = 1; i < V; i++) {printf("d - d \td \n", parent[i], i, graph[i][parent[i]]);}void primMST(int graph[V][V]) {int parent[V]; // 存放最小生成树的结点int key[V]; // 存放与最小生成树相邻的边的权值bool mstSet[V]; // 存放已访问的顶点for (int i = 0; i < V; i++) {key[i] = INT_MAX, mstSet[i] = false;}key[0] = 0;parent[0] = -1;for (int count = 0; count < V - 1; count++) {int u = minKey(key, mstSet);mstSet[u] = true;for (int v = 0; v < V; v++) {if (graph[u][v] !mstSet[v] graph[u][v] < key[v]) { parent[v] = u, key[v] = graph[u][v];}}}printMST(parent, graph); }int m本人n() {int graph[V][V] = {{0, 2, 0, 6, 0},{2, 0, 3, 8, 5},{0, 3, 0, 0, 7},{6, 8, 0, 0, 9},{0, 5, 7, 9, 0}};primMST(graph);return 0;}```四、代码说明1. minKey函数该函数用于在尚未加入最小生成树的结点中找到与最小生成树相邻的边中权值最小的结点。

最小生成树的Prim算法以及Kruskal算法的证明

最小生成树的Prim算法以及Kruskal算法的证明

最⼩⽣成树的Prim算法以及Kruskal算法的证明Prime算法的思路:从任何⼀个顶点开始,将这个顶点作为最⼩⽣成树的⼦树,通过逐步为该⼦树添加边直到所有的顶点都在树中为⽌。

其中添加边的策略是每次选择外界到该⼦树的最短的边添加到树中(前提是⽆回路)。

Prime算法的正确性证明:引理1:对于连通图中的顶点vi,与它相连的所有边中的最短边⼀定是属于最⼩⽣成树的。

引理2:证明:假设最⼩⽣成树已经建成;(vi, vj)是连接到顶点vi的最短边,在最⼩⽣成树中取出vi,断开连接到vi的边,则⽣成树被拆分成1、顶点vi2、顶点vj所在的连通分量(单独⼀个顶点也看作⼀个独⽴的连通分量)3、其余若⼲个连通分量(个数⼤于等于0)三个部分现在要重建⽣成树,就要重新连接之前被断开的各边虽然不知道之前被断开的都是哪⼏条边,但是可以通过这样⼀个简单的策略来重建连接:将vi分别以最⼩的成本逐个连接到这若⼲个互相分离的连通分量;具体来说,就是要分别遍历顶点vi到某个连通分量中的所有顶点的连接,然后选择其中最短的边来连接vi和该连通分量;⽽要将vi连接到vj所在的连通分量,显然通过边(vi, vj)连接的成本最低,所以边(vi, vj)必然属于最⼩⽣成树(如果连接到vi的最短边不⽌⼀条,只要任意挑选其中的⼀条(vi, vj)即可,以上的证明对于这种情况同样适⽤)。

这样我们就为原来只有⼀个顶点vi的⼦树添加了⼀个新的顶点vj及新边(vi, vj);接下来只要将这棵新⼦树作为⼀个连通⼦图,并且⽤这个连通⼦图替换顶点vi重复以上的分析,迭代地为⼦树逐个地添加新顶点和新边即可。

Kruskal算法:通过从⼩到⼤遍历边集,每次尝试为最⼩⽣成树加⼊当前最短的边,加⼊成功的条件是该边不会在当前已构建的图中造成回路,当加⼊的边的数⽬达到n-1,遍历结束。

Kruskal算法的正确性证明:Kruskal算法每次为当前的图添加⼀条不会造成回路的新边,其本质是逐步地连接当前彼此分散的各个连通分量(单个顶点也算作⼀个连通分量),⽽连接的策略是每次只⽤最⼩的成本连接任意两个连通分量。

数学建模-最小生成树-kruskal算法及各种代码

数学建模-最小生成树-kruskal算法及各种代码

kruskal算法及代码---含伪代码、c代码、matlab、pascal等代码K r u s k a l算法每次选择n- 1条边,所使用的贪婪准则是:从剩下的边中选择一条不会产生环路的具有最小耗费的边加入已选择的边的集合中。

注意到所选取的边若产生环路则不可能形成一棵生成树。

K r u s k a l算法分e 步,其中e 是网络中边的数目。

按耗费递增的顺序来考虑这e 条边,每次考虑一条边。

当考虑某条边时,若将其加入到已选边的集合中会出现环路,则将其抛弃,否则,将它选入。

目录Kruskal算法Kruskal算法的代码实现Kruskal算法Kruskal算法的代码实现算法定义克鲁斯卡尔算法假设 WN=(V,{E}) 是一个含有 n 个顶点的连通网,则按照克鲁斯卡尔算法构造最小生成树的过程为:先构造一个只含 n 个顶点,而边集为空的子图,若将该子图中各个顶点看成是各棵树上的根结点,则它是一个含有 n 棵树的一个森林。

之后,从网的边集 E 中选取一条权值最小的边,若该条边的两个顶点分属不同的树,则将其加入子图,也就是说,将这两个顶点分别所在的两棵树合成一棵树;反之,若该条边的两个顶点已落在同一棵树上,则不可取,而应该取下一条权值最小的边再试之。

依次类推,直至森林中只有一棵树,也即子图中含有n-1条边为止。

举例描述克鲁斯卡尔算法(Kruskal's algorithm)是两个经典的最小生成树算法的较为简单理解的一个。

这里面充分体现了贪心算法的精髓。

大致的流程可以用一个图来表示。

这里的图的选择借用了Wikipedia上的那个。

非常清晰且直观。

首先第一步,我们有一张图,有若干点和边如下图所示:第一步我们要做的事情就是将所有的边的长度排序,用排序的结果作为我们选择边的依据。

这里再次体现了贪心算法的思想。

资源排序,对局部最优的资源进行选择。

排序完成后,我们率先选择了边AD。

这样我们的图就变成了第二步,在剩下的变中寻找。

② kruskal最小生成树算法的定义和实现

② kruskal最小生成树算法的定义和实现

Krskal最小生成树算法是一种用来解决图论中最小生成树问题的算法。

它采用了一种贪心的策略,即每一步都选择权值最小的边,并且保证选择的边不会构成环,直到生成一棵最小生成树为止。

1. 算法的定义kruskal最小生成树算法的定义如下:输入:一个连通无向图G=(V,E),其中V是顶点集合,E是边的集合,每条边都有一个权值。

输出:G的最小生成树算法步骤:① 将图G的所有边按照权值从小到大进行排序。

② 初始化一个空的边集合T,该集合用来存放最小生成树的边。

③ 依次遍历排序后的边集合,对于每一条边e=(u,v),如果将该边添加到T中不会构成环,则将其添加到T中。

④ 重复步骤③,直到T中的边数等于V-1为止,此时T就是G的最小生成树。

2. 算法的实现下面是kruskal最小生成树算法的具体实现过程:```pythonclass UnionFind:def __init__(self, n):self.parent = [i for i in range(n)]def find(self, x):if self.parent[x] != x:self.parent[x] = self.find(self.parent[x]) return self.parent[x]def union(self, x, y):root_x = self.find(x)root_y = self.find(y)if root_x != root_y:self.parent[root_x] = root_ydef kruskal(graph):edges = []for u in range(len(graph)):for v in range(u+1, len(graph)):if graph[u][v] != 0:edges.append((u, v, graph[u][v])) edges.sort(key=lambda x: x[2])n = len(graph)result = []uf = UnionFind(n)for edge in edges:u, v, weight = edgeif uf.find(u) != uf.find(v):uf.union(u, v)result.append((u, v, weight))if len(result) == n - 1:breakreturn result```以上代码实现了kruskal最小生成树算法,其中使用了UnionFind类来实现并查集的功能,以判断是否构成环。

最小生成树---普里姆算法(Prim算法)和克鲁斯卡尔算法(Kruskal算法)

最小生成树---普里姆算法(Prim算法)和克鲁斯卡尔算法(Kruskal算法)

最⼩⽣成树---普⾥姆算法(Prim算法)和克鲁斯卡尔算法(Kruskal算法)最⼩⽣成树的性质:MST性质(假设N=(V,{E})是⼀个连通⽹,U是顶点集V的⼀个⾮空⼦集,如果(u,v)是⼀条具有最⼩权值的边,其中u属于U,v属于V-U,则必定存在⼀颗包含边(u,v)的最⼩⽣成树)普⾥姆算法(Prim算法)思路:以点为⽬标构建最⼩⽣成树1.将初始点顶点u加⼊U中,初始化集合V-U中各顶点到初始顶点u的权值;2.根据最⼩⽣成树的定义:从n个顶点中,找出 n - 1条连线,使得各边权值最⼩。

循环n-1次如下操作:(1)从数组lowcost[k]中找到vk到集合U的最⼩权值边,并从数组arjvex[k] = j中找到该边在集合U中的顶点下标(2)打印此边,并将vk加⼊U中。

(3)通过查找邻接矩阵Vk⾏的各个权值,即vk点到V-U中各顶点的权值,与lowcost的对应值进⾏⽐较,若更⼩则更新lowcost,并将k存⼊arjvex数组中以下图为例#include<bits/stdc++.h>using namespace std;#define MAXVEX 100#define INF 65535typedef char VertexType;typedef int EdgeType;typedef struct {VertexType vexs[MAXVEX];EdgeType arc[MAXVEX][MAXVEX];int numVertexes, numEdges;}MGraph;void CreateMGraph(MGraph *G) {int m, n, w; //vm-vn的权重wscanf("%d %d", &G->numVertexes, &G->numEdges);for(int i = 0; i < G->numVertexes; i++) {getchar();scanf("%c", &G->vexs[i]);}for(int i = 0; i < G->numVertexes; i++) {for(int j = 0; j < G->numVertexes; j++) {if(i == j) G->arc[i][j] = 0;else G->arc[i][j] = INF;}}for(int k = 0; k < G->numEdges; k++) {scanf("%d %d %d", &m, &n, &w);G->arc[m][n] = w;G->arc[n][m] = G->arc[m][n];}}void MiniSpanTree_Prim(MGraph G) {int min, j, k;int arjvex[MAXVEX]; //最⼩边在 U集合中的那个顶点的下标int lowcost[MAXVEX]; // 最⼩边上的权值//初始化,从点 V0开始找最⼩⽣成树Tarjvex[0] = 0; //arjvex[i] = j表⽰ V-U中集合中的 Vi点的最⼩边在U集合中的点为 Vjlowcost[0] = 0; //lowcost[i] = 0表⽰将点Vi纳⼊集合 U ,lowcost[i] = w表⽰ V-U中 Vi点到 U的最⼩权值for(int i = 1; i < G.numVertexes; i++) {lowcost[i] = G.arc[0][i];arjvex[i] = 0;}//根据最⼩⽣成树的定义:从n个顶点中,找出 n - 1条连线,使得各边权值最⼩for(int i = 1; i < G.numVertexes; i++) {min = INF, j = 1, k = 0;//寻找 V-U到 U的最⼩权值minfor(j; j < G.numVertexes; j++) {// lowcost[j] != 0保证顶点在 V-U中,⽤k记录此时的最⼩权值边在 V-U中顶点的下标if(lowcost[j] != 0 && lowcost[j] < min) {min = lowcost[j];k = j;}}}printf("V[%d]-V[%d] weight = %d\n", arjvex[k], k, min);lowcost[k] = 0; //表⽰将Vk纳⼊ U//查找邻接矩阵Vk⾏的各个权值,与lowcost的对应值进⾏⽐较,若更⼩则更新lowcost,并将k存⼊arjvex数组中for(int i = 1; i < G.numVertexes; i++) {if(lowcost[i] != 0 && G.arc[k][i] < lowcost[i]) {lowcost[i] = G.arc[k][i];arjvex[i] = k;}}}int main() {MGraph *G = (MGraph *)malloc(sizeof(MGraph));CreateMGraph(G);MiniSpanTree_Prim(*G);}/*input:4 5abcd0 1 20 2 20 3 71 2 42 3 8output:V[0]-V[1] weight = 2V[0]-V[2] weight = 2V[0]-V[3] weight = 7最⼩总权值: 11*/时间复杂度O(n^2)克鲁斯卡尔算法(Kruskal算法)思路:以边为⽬标进⾏构建最⼩⽣成树在边集中依次寻找最⼩权值边,若构建是不形成环路(利⽤parent数组记录各点的连通分量),则将其添加到最⼩⽣成树中。

km算法bfs写法详解

km算法bfs写法详解

km算法bfs写法详解在计算机科学中,广度优先搜索(BFS)是一种用于图或树的搜索算法。

这种方法通常用于在图或树中找到最短路径、查找特定节点或解决其他类似问题。

其中,Kruskal算法是一种用于确定加权无向图的最小生成树的BFS算法。

下面,我们将详细介绍Kruskal算法的BFS实现方法。

一、算法概述Kruskal算法通过BFS来寻找加权无向图的最小生成树。

该算法将图中的边按照权重从小到大进行排序,然后逐一选择边并添加到生成树中。

在添加每条边之前,算法会检查所选边是否与已选择的边形成一个环。

如果形成环,则舍弃该边;否则,将该边添加到生成树中。

通过这种方式,Kruskal算法可以避免生成包含环的生成树,从而确保得到正确的结果。

二、BFS实现步骤1.创建并初始化一个空的当前生成树(tree)和一个空的已选择边集合(selected)。

2.从所有边中随机选择一条边(记为edge),将其添加到已选择边集合中,并将其加入当前生成树中。

3.将所选边的两个顶点分别加入当前生成树的左右两个顶点集合中。

4.遍历所选边的所有未访问的顶点邻居(即不在当前生成树中的顶点),将它们加入到已选择边集合中,并检查是否与当前生成树的左右两个顶点集合中的任意一个有边相连。

如果有,则跳过该顶点;否则,将该顶点加入当前生成树的右顶点集合中。

5.如果当前生成树的左右两个顶点集合的大小不相等,说明存在环,需要重新选择边并重新检查顶点。

6.如果遍历完所有边后仍未找到环,则当前生成树即为最小生成树。

7.返回最小生成树。

三、代码实现以下是一个Python实现的Kruskal算法的BFS示例代码:```pythonclassGraph:def__init__(self,vertices):self.V=verticesself.graph=[]#邻接列表表示的图defadd_edge(self,u,v,w):self.graph.append([u,v,w])#BFS的实现defkruskal_bfs(self):result=[]#存放生成树的所有边i,e=0,0#迭代器和边的数量初始化为0pairs=[]#存放边的信息fornodeinrange(self.V):#将所有的边按照权重从小到大排序ifi<len(self.graph)andself.graph[i][2]<e:#如果这条边的权重小于已选择的边的最大权重pairs.append(self.graph[i])#将这条边加入到已选择的边集合中e+=1#更新已选择的边的最大权重i+=1#遍历下一个节点pairs=sorted(pairs,key=lambdaitem:item[2])#对边的权重进行排序foredgeinpairs:#逐一选择边并添加到生成树中ifnot(edge[0]inresultandedge[1]inresult):#检查所选边是否已经存在于生成树中,避免形成环result.append(edge)#将所选边添加到生成树中returnresult#返回生成树的所有边```四、总结Kruskal算法的BFS实现方法通过逐一选择边并检查是否形成环,可以有效地确定加权无向图的最小生成树。

克鲁斯卡尔算法求最小生成树完整代码

克鲁斯卡尔算法求最小生成树完整代码

克鲁斯卡尔算法是一种用来求解最小生成树(Minimum Spanning Tree)的经典算法,它采用了贪心策略,能够高效地找到图中的最小生成树。

下面将为大家介绍克鲁斯卡尔算法的完整代码,希望对大家有所帮助。

1. 算法思路克鲁斯卡尔算法的基本思路是:首先将图中的所有边按照权值进行排序,然后从小到大依次考虑每条边,如果加入该边不会构成环,则将其加入最小生成树中。

在算法执行过程中,我们需要使用并查集来判断是否会构成环。

2. 代码实现接下来,我们将给出克鲁斯卡尔算法的完整代码,代码使用C++语言编写,具体如下:```cpp#include <iostream>#include <vector>#include <algorithm>using namespace std;// 定义图的边struct Edge {int u, v, weight;Edge(int u, int v, int weight) : u(u), v(v), weight(weight) {} };// 定义并查集class UnionFind {private:vector<int> parent;public:UnionFind(int n) {parent.resize(n);for (int i = 0; i < n; i++) {parent[i] = i;}}int find(int x) {if (parent[x] != x) {parent[x] = find(parent[x]);}return parent[x];}void Union(int x, int y) {int root_x = find(x);int root_y = find(y);if (root_x != root_y) {parent[root_x] = root_y;}}};// 定义比较函数用于排序bool cmp(const Edge a, const Edge b) {return a.weight < b.weight;}// 克鲁斯卡尔算法vector<Edge> kruskal(vector<Edge> edges, int n) { // 先对边进行排序sort(edges.begin(), edges.end(), cmp);// 初始化最小生成树的边集vector<Edge> res;UnionFind uf(n);for (int i = 0; i < edges.size(); i++) {int u = edges[i].u, v = edges[i].v, weight = edges[i].weight; // 判断是否构成环if (uf.find(u) != uf.find(v)) {uf.Union(u, v);res.push_back(edges[i]);}}return res;}// 测试函数int m本人n() {vector<Edge> edges;edges.push_back(Edge(0, 1, 4));edges.push_back(Edge(0, 7, 8));edges.push_back(Edge(1, 2, 8));edges.push_back(Edge(1, 7, 11));edges.push_back(Edge(2, 3, 7));edges.push_back(Edge(2, 5, 4));edges.push_back(Edge(2, 8, 2));edges.push_back(Edge(3, 4, 9));edges.push_back(Edge(3, 5, 14));edges.push_back(Edge(4, 5, 10));edges.push_back(Edge(5, 6, 2));edges.push_back(Edge(6, 7, 1));edges.push_back(Edge(6, 8, 6));edges.push_back(Edge(7, 8, 7));vector<Edge> res = kruskal(edges, 9);for (int i = 0; i < res.size(); i++) {cout << res[i].u << " " << res[i].v << " " << res[i].weight << endl;}return 0;}```3. 算法实例上述代码实现了克鲁斯卡尔算法,并对给定的图进行了最小生成树的求解。

最小生成树问题例题

最小生成树问题例题

最小生成树问题例题最小生成树(Minimum Spanning Tree)是图论中的一个经典问题,它是指在一个带权无向图中找到一棵生成树,使得树上所有边的权值之和最小。

最小生成树问题在实际生活中有着广泛的应用,比如电力输送、通信网络等领域。

下面我们以一个具体的例子来说明最小生成树问题的求解过程。

假设有一个无向图,图中包含了6个节点(A、B、C、D、E、F)和9条边。

每条边都有一个权值,表示连接两个节点的成本。

我们的目标是找到一棵最小生成树。

首先,我们可以使用 Prim 算法来求解最小生成树。

Prim 算法的基本思想是从一个起始节点开始,逐步扩展生成树,直到包含所有节点为止。

具体步骤如下:1. 选择一个起始节点,将其标记为已访问。

2. 从已访问的节点中,选择一条连接到未访问节点的最短边。

3. 将这条边加入到最小生成树中,并将连接的节点标记为已访问。

4. 重复步骤2和步骤3,直到所有节点都被访问过。

根据上述算法,我们可以依次选取边 AB、CD、BC、EF、DE 来构建最小生成树。

最终的最小生成树是:A-B、C-D、B-C、E-F 和 D-E 这五条边,它们的权值之和为12。

另外一个常用的求解最小生成树问题的算法是 Kruskal 算法。

Kruskal 算法的基本思想是将图中的边按照权值从小到大进行排序,然后依次选取边,如果这条边连接的两个节点不在同一个连通分量中,就将这条边加入到最小生成树中。

具体步骤如下:1. 对图中的边按照权值进行排序。

2. 从权值最小的边开始,依次选取边。

3. 如果选取的边连接的两个节点不在同一个连通分量中,就将这条边加入到最小生成树中,并将连接的节点合并为一个连通分量。

4. 重复步骤2和步骤3,直到最小生成树中包含了所有的节点。

使用 Kruskal 算法求解上述例子,我们可以依次选取边 AB、BC、CD、DE、EF 来构建最小生成树。

最终的最小生成树是:A-B、B-C、C-D、D-E 和 E-F 这五条边,它们的权值之和也是12。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

Kruskal算法完成POJ1258
//by Guo Wei #include <iostream> #include <vector> #include <algorithm> using namespace std; struct Edge { int s,e,w; //起点,终点,权值 Edge(int ss,int ee,int ww):s(ss),e(ee),w(ww) { } Edge() { } bool operator < (const Edge & e1) const { return w < e1.w; } }; vector <Edge> edges; vector <int> parent;

图例
关键问题

每次如何从连接T中和T外顶点的所有边中,找 到一条最短的 1) 如果用邻接矩阵存放图,而且选取最短边的 时候遍历所有点进行选取,则总时间复杂度为 O(V2), V 为顶点个数 2)用邻接表存放图,并使用堆来选取最短边,则 总时间复杂度为O(ElogV) 不加堆的Prim 算法适用于密集图,加堆的适用 于稀疏图
算法:Kruskal 和 Prim

Kruskal:将所有边从小到大加入,在此过程中 判断是否构成回路
– – –
使用数据结构:并查集 时间复杂度:O(ElogE) 适用于稀疏图 使用数据结构:堆 时间复杂度:O(ElogV) 或 O(VlogV+E)(斐波那契堆) 适用于密集图 若不用堆则时间复杂度为O(V2)
只需在该图上求最小生成树,d 的最小值即为 第 K 长边! 因为:最小生成树中的最长k-1条长边都去掉 后,正好将原树分成了k 个连通分支,在每 个连通分支上摆一个卫星设备即可
为什么d不可能比第k长边更小?
假设最小生成树T上,第k长边连接的点是a,b,那么 将边<a,b>去掉后,树就分成了两个部分T1 和T2 要使T1和T2能够通讯,必须在T1中找一点p和T2中 的点q相连,若边<p,q>的长度小于<a,b>,则在T 上用<p,q>替换<a,b>就能得到更小的生成树,矛 盾。因此找不到长度小于<a,b>的<p,q>。 对任何比第k长边短的边e,同理也不可能找到替代e 的边。
本文大量内容引自北京大学信息学院程序设计实习(实验班)郑聃崴、 陈国鹏等同学的讲义,在此致谢
图的生成树


在一个连通图G中,如果取它的全部顶点和 一部分边构成一个子图G’,即: V(G’)=V(G);E(G’) ∈E(G) 若边集E(G’)中的边既将图中的所有顶点连 通又不形成回路,则称子图G’是原图G的一 棵生成树。 一棵含有n个点的生成树,必含有n-1条边。
首先从V中任取一个顶点(假定取v1),将它并入 U中,此时U={v1},然后只要U是V的真子集(U∈V), 就从那些一个端点已在T中,另一个端点仍在T外 的所有边中,找一条最短边,设为(vi,vj),其中 vi∈U,vj ∈V-U,并把该边(vi, vj)和顶点vj分别并入T 的边集TE和顶点集U,如此进行下去,每次往生成 树里并入一个顶点和一条边,直到n-1次后得到最 小生成树。
Kruskal算法

假设G=(V,E)是一个具有n个顶点的连通网, T=(U,TE)是G的最小生成树,U=V,TE初值为 空。
将图G中的边按权值从小到大依次选取,若 选取的边使生成树不形成回路,则把它并 入TE中,若形成回路则将其舍弃,直到TE 中包含N-1条边为止,此时T为最小生成树。

关键问题
int main() { int N; while(cin >> N) { for( int i = 0;i < N; ++i) G[i].clear(); for( int i = 0; i < N; ++i) for( int j = 0; j < N; ++j) { int w; cin >> w; G[i].push_back(Edge(j,w)); } cout << HeapPrim(G,N) << endl; } }
因此 d不可能更小了
最小生成树可能不止一棵,为什么第k长边长度一定相同?因为有 以下结论:

一个图的两棵最小生成树,边的权值序列 排序后结果相同
证明:假设某个最小生成树T1的边权从小到大排序后的序列为: a1, a2 …. an 某个最小生成树T2的边权从小到大排序后的序列为: b1,b2…bn 两者若不同,则必然存在一个最小的i,使得 ai > bi 假设T2中有m条边的权为bi,那么,T1中最多只有m-1条边的权和bi相同。 但是对于T2中任何一条不在T1中的权为bi的边,如果将其从T2去掉,则T2被分成A,B两 个部分。那么在T1中连接A,B这两个部分的边,必然权值是等于bi的,否则经过替换, 要么T1的权值可以变得更小,要么T2的权值可以变得更小,这和T1,T2是最小生成树矛 盾。对T2中每个权值为bi的边,都可以在T1中找到一个权值相同且不在T2的边与其对 应,而这些边由于是连接不同部分的,所以不可能相同,因此,在T1中也应该有m条权 值为bi的边,这和T1中最多m-1条权值为bi的边矛盾。因此,不存在i,使得的ai>bi,即两 个边权序列应该相同。


如何判断欲加入的一条边是否与生成树 中边构成回路。 将各顶点划分为所属集合的方法来解决, 每个集合的表示一个无回路的子集。开 始时边集为空,N个顶点分属N个集合, 每个集合只有一个顶点,表示顶点之间 互不连通。 当从边集中按顺序选取一条边时,若它 的两个端点分属于不同的集合,则表明 此边连通了两个不同的部分,因每个部 分连通无回路,故连通后仍不会产生回 路,此边保留,同时把相应两个集合合 并

Prim:从任一节点出发,不断扩展
– – – –
例题: POJ 2349 Arctic Network




某地区共有n座村庄,每座村庄的坐标用一对整数(x, y)表示,现在要在村庄之间建立通讯网络。 通讯工具有两种,分别是需要铺设的普通线路和无 线通讯的卫星设备。 只能给k个村庄配备卫星设备,拥有卫星设备的村 庄互相间直接通讯。 铺设了线路的村庄之间也可以通讯。但是由于技术 原因,两个村庄之间线路长度最多不能超过 d, 否则 就会由于信号衰减导致通讯不可靠。要想增大 d 值, 则会导致要投入更多的设备(成本)
北京大学暑期课《ACM/ICPC竞赛训练》
北京大学信息学院 郭炜 guo_wei@ /guoweiofpku
课程网页:/summerschool/pku_acm_train.htm
最小生成树(MST)问题
int main() { int N; while(cin >> N) { parent.clear(); edges.clear(); for( int i = 0;i < N; ++i) parent.push_back(i); for( int i = 0; i < N; ++i) for( int j = 0; j < N; ++j) { int w; cin >> w; edges.push_back(Edge(i,j,w)); } sort(edges.begin(),edges.end()); int done = 0; int totalLen = 0; for( int i = 0;i < edges.size(); ++i) { if( GetRoot(edges[i].s) != GetRoot(edges[i].e)) { Merge(edges[i].s,edges[i].e); ++done; totalLen += edges[i].w; } if( done == N – 1) break; } cout << totalLen << endl; } }
while( nDoneNum < n && !pq.empty() ) { do {//每次从队列里面拿离在建生成树最近的点 xDist = pq.top(); pq.pop(); } while( vUsed[xDist.v] == 1 && ! pq.empty()); if( vUsed[xDist.v] == 0 ) { nTotalW += xDist.w; vUsed[xDist.v] = 1; nDoneNum ++; for( i = 0;i < G[xDist.v].size();i ++ ) {//更新新加入点的邻点 int k = G[xDist.v][i].v; if( vUsed[k] == 0) { int w = G[xDist.v][i].w ; if( vDist[k] > w ) { vDist[k] = w; pq.push(Edge(k,w)); } } } } } if( nDoneNum < n ) return -1; //图不连通 return nTotalW; }


POJ 1258 最小生成树模版题
输入图的邻接矩阵,求最小生成树的总权值 (多组数据) 输入样例: 4 0 4 9 21 4 0 8 17 9 8 0 16 21 17 16 0 输出样例: 28
prioirty_queue实现 Prim + 堆 完成POJ1258
//by Guo Wei #include <iostream> #include <vector> #include <algorithm> #include <queue> using namespace std; const int INFINITE = 1 << 30; struct Edge { int v; //边端点,另一端点已知 int w; //边权值,也用来表示v到在建最小生成树的距离 Edge(int v_ = 0, int w_ = INFINITE):v(v_),w(w_) { } bool operator <(const Edge & e) const { return w > e.w; //在队列里,边权值越小越优先 } }; vector< vector <Edge> > G(110); //图的邻接表
相关文档
最新文档