Minimum Spanning Tree
题目链接
click here!
题目大意
最小生成树模板题,最小生成树,指的是在图中选择所有的顶点和一些边,使得所有的顶点都互相可达,并且总的边的权值之和是所有使全部顶点相互可达的连接方案中边权值之和最小的那个方案
输入
图的顶点个数n
接下来n行,每行有n个数,表示vi到vj的边的权值
输出
最小生成树的权值之和
思路
最小生成树有Prim算法和Kruskal算法两种,这里我们采用Prim算法:
Prim算法的核心:
Prim算法实际上是一种贪心算法,从某个点开始,我们优先选择该点直接可达的所有顶点中边权值最小的那一个,把这个顶点加入到树中,再在剩下的所有顶点中选择到当前这颗树的边权值最小的顶点…直到所有的顶点都被加入到树中。显然,由于我们每次都选择了当前情况下最小的顶点,那么总的边权值就是最小的
Prim算法的操作:
1.从某个出发点开始,把该出发点作为树的第一个结点,把这个出发点所有的邻接其他所有点的信息先加入到一个数组中
2.选择当前数组中的最小值
3.维护这个数组,当出现当前树中的某个顶点到未被加入树的定点的距离小于对应顶点在数组中的值时,就更新数组中的值
4.重复2-3直到所有的顶点都被加入到树中
考虑输入的规模为n很小,采用邻接矩阵建图即可
邻接矩阵:如果一个图有n个顶点,那么我们就可以建立一个n*n的邻接矩阵,矩阵第i行第j列的值只能0或1,为0表示vi与vj没有边,反之则有边
复杂度分析
时间复杂度:建图O(n) + Prim,对于n个顶点要选择n - 1条边,因此最外层循环n - 1,每次都要查找到当前最小的权值的那条边,需要n次查找,之后要逐个比对更新,每次要查找n个顶点,因此Prim的总的时间复杂度为O((n - 1) * (2n)) = O(n^2)
总的时间复杂度为O(n + n^2) = O(n^2)
空间复杂度: 由于我们采取邻接矩阵存储图,空间复杂度为点的个数的平方,即O(n^2)
AC参考代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
#define INF 0x3f3f3f3f
using namespace std;
typedef struct Graph {
int vexnums;
int** edge;
};
int Prim(Graph G) {
vector<int> lowcost;
for (int i = 0; i < G.vexnums; i++) {
lowcost.push_back(G.edge[0][i]);
}
lowcost[0] = -1;
int sum = 0;
for (int i = 1; i < G.vexnums; i++) {
int j = 0,min = INF,k = 0;
while (j < G.vexnums) {
if (lowcost[j] != -1 && lowcost[j] < min) {
min = lowcost[j];
k = j;
}
j++;
}
lowcost[k] = -1;
sum += min;
for (int x = 0; x < G.vexnums; x++) {
if (lowcost[x] != -1 && G.edge[k][x] < lowcost[x]) {
lowcost[x] = G.edge[k][x];
}
}
}
return sum;
}
int main()
{
Graph G;
cin >> G.vexnums;
G.edge = new int* [G.vexnums];
for (int i = 0; i < G.vexnums; i++) {
G.edge[i] = new int[G.vexnums];
}
for (int x = 0; x < G.vexnums; x++) {
for (int y = 0; y < G.vexnums; y++) {
cin >> G.edge[x][y];
if (G.edge[x][y] == -1) G.edge[x][y] = INF;
}
}
cout << Prim(G) << endl;
}