一、最小生成树
- 一个有 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 <iostream>
#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;
}
蒟蒻一只,欢迎指正