C/C++ 最小生成树—Prim算法

一、最小生成树

  • 一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。
  • 在一给定的无向图G = (V, E) 中,(u, v) 代表连接顶点 u 与顶点 v 的边,而 w(u, v) 代表此边的权重,若存在 T 为 E 的子集且为无循环图,使得
    的 w(T) 最小,则此 T 为 G 的最小生成树。
  • 最小生成树其实是最小权重生成树的简称。

二、Prim算法(普里姆算法)

构造过程

假设N = (V, E)是连通网,TE是N上最小生成树中边的集合。

  • U = {u0}(u0∈V), TE = {};
  • 在所有u∈U,v∈V-U的边(u, v)∈E中找一条权值最小的边(u0, v0)并入集合TE,同时v0并入U;
  • 重复第二步直至U = V为止。
  • 此时TE中必有n-1条边,则T = (V, TE)为N的最小生成树;

算法步骤

  • 首先将初始化顶点u加入U中,对其余的每一个顶点vj,将closedge[j]均初始化为到u的边信息。
  • 循环n-1次,做如下处理:
    • 从各组边closedge中选出最小边closedge[k],输出此边;
    • 将k加入U中;
    • 更新剩余的每组最小边信息closedge[j],对于V-U中的边,新增加了一条从k到j的边,如果新边的权值比closedge[j].lowcost小,则将closedge[j].lowcost更新为新边的权值。

三、举一个栗子(镖局运镖)

Description

最近小哼迷上了《龙门镖局》,从恰克图道武夷山,从张家口道老河口,从迪化道佛山,从蒙自道奉天…古代镖局的运镖,也就是现在的物流。镖局每到一个地方开展业务,都需要堆运镖途中的绿林好汉进行打点(不给钱就不让过路)。好说话的打点费就比较低,不好说话的打点费就比较高。
城镇类似如下,顶点是城镇编号,边上的值表示这条道路上打点绿林好汉需要的银子数。请你帮镖局算算,如果从1号城镇出发,遍历每个城镇最少需要准备多少打点银子?

Input

  • 输入第一行包括两个正整数n,m。n代表城镇个数,m表示道路个数;
  • 后面的m行分别包含三个正整数a,b,c,表示在从城镇a 到 城镇b的道路上需要交纳的银子数;

Output

输出从1号城镇出发,遍历每个城镇最少需要准备的银子数。

Sample Input

6 9
2 4 11
3 5 13
4 6 3
5 6 4
2 3 6
4 5 7
1 2 1
3 4 9
1 3 2

Sample Output

19

More Info

(1 < n,m < 100)

Code

详见注释

#include 
#define Max 103

using namespace std;

typedef struct AMGraph {				//图的结构体
	int ves[Max];						//顶点表
	int arcs[Max][Max];					//邻接矩阵
	int vexnum, arcnum;					//顶点数和边数
};

typedef struct Gedge {					
	int adjvex;							//最小边的前驱点
	int lowcost;						//最小边的权值
};
Gedge closedge[Max];
int sum = 0;							//最小生成树权值和

void putin(AMGraph &G)					//初始化并输入图
{
	int u, v, w;

	cin >> G.vexnum >> G.arcnum;
	for (int i = 1; i <= G.vexnum; i++)				//初始化邻接矩阵值
	{
		for (int j = 1; j <= G.vexnum; j++)
			G.arcs[i][j] = 0xcffffff;
	}
	for (int i = 1; i <= G.arcnum; i++)				//输入边和其权值
	{
		cin >> u >> v >> w;
		G.arcs[u][v] = G.arcs[v][u] = w;
	}
}

void Prim(AMGraph &G)								//普利姆算法
{
	for (int i = 1; i <= G.vexnum; i++)				//起始点默认为1
		if (i != 1) closedge[i] = { 1, G.arcs[1][i] };
	closedge[1].lowcost = 0;						//初始化最小边集合
	
	for (int i = 2; i <= G.vexnum; i++)				//遍历n-1次
	{
		int k, min=0xcffffff;
		for (int j = 1; j <= G.vexnum; j++)			//找到当前的最小边的顶点
		{
			if (closedge[j].lowcost < min && closedge[j].lowcost != 0) {
				min = closedge[j].lowcost;
				k = j;
			}
		}
		//cout << closedge[k].adjvex << " " << k << endl;
		sum += closedge[k].lowcost;					//把最小边的权值求和
		closedge[k].lowcost = 0;					//把找到的最小边并入集合
		for (int j = 1; j <= G.vexnum; j++)			//刷新当前未并入集合的边
		{
			if (G.arcs[k][j] < closedge[j].lowcost) {
				closedge[j].adjvex = k;
				closedge[j].lowcost = G.arcs[k][j];
			}
				
		}
	}
}

int main()
{
	AMGraph G;
	
	putin(G);
	Prim(G);
	cout << sum << endl;							//输出要求的最小生成树的权值和
	return 0;
}

蒟蒻一只,欢迎指正

你可能感兴趣的:(数据结构,图论,prim算法,数据结构,严蔚敏,C/C++,最小生成树)