1.概述
设G =(V,E)是无向连通带权图,即一个网络。E中每条边(v,w)的权为c[v][w]。如果G的子图G’是一棵包含G的所有顶点的树,则称G’为G的生成树。生成树上各边权的总和称为该生成树的耗费。在G的所有生成树中,耗费最小的生成树称为G的最小生成树。
网络的最小生成树在实际中有广泛应用。例如,在设计通信网络时,用图的顶点表示城市,用边(v,w)的权c[v][w]表示建立城市v和城市w之间的通信线路所需的费用,则最小生成树就给出了建立通信网络的最经济的方案。
最小生成树性质:
设G=(V,E)是连通带权图,U是V的真子集。如果(u,v)属于E,且u属于U,v属于V-U,且在所有这样的边中,(u,v)的权c[u][v]最小,那么一定存在G的一棵最小生成树,它以(u,v)为其中一条边。这个性质有时也称为MST性质。
2.PRIM算法
设G=(V,E)是连通带权图,V={1,2,…,n}。
构造G的最小生成树的Prim算法的基本思想是:首先置S={1},然后,只要S是V的真子集,就作如下的贪心选择:选取满足条件i属于S,j属于V-S,且c[i][j]最小的边,将顶点j添加到S中。这个过程一直进行到S=V时为止。
在这个过程中选取到的所有边恰好构成G的一棵最小生成树。
利用最小生成树性质和数学归纳法容易证明,上述算法中的边集合T始终包含G的某棵最小生成树中的边。因此,在算法结束时,T中的所有边构成G的一棵最小生成树。
例如,对于右图中的带权图,按Prim算法选取边的过程如下页图所示。
按Prim算法选取边的过程如下页图所示。
在上述Prim算法中,还应当考虑如何有效地找出满足条件i在S中,j也在V-S中,且权c[i][j]最小的边(i,j)。实现这个目的的较简单的办法是设置2个数组closest和lowcost。
在Prim算法执行过程中,先找出V-S中使lowcost值最小的顶点j,然后根据数组closest选取边(j,closest[j]),最后将j添加到S中,并对closest和lowcost作必要的修改。
用这个办法实现的Prim算法所需的计算时间为O(n2).
其实这个算法和迪杰斯特拉算法非常相似,都是每次都要取目前已知的到达未连通结点的最小权值,当加入该点之后再刷新lowcost数组和closest数组,对比看是否未加入树的点通过该刚加入树的点是否有更短的路径也就是更小的权值,若有则刷新,若没有则不处理,以保证lowcost和closest数组里的数据都是根据当前已知情况得到的最小权值数组和该顶点最接近的顶点编号。
图是搬运的,因为自己懒得做。
代码如下:
#include<iostream>
#include<vector>
using namespace std;
void prim(vector<vector<int>> &VGraph, vector<int> &lowcost, vector<int> &closest, vector<bool> &visited) //思路类似于迪杰斯特拉算法
{
int size = lowcost.size();
visited[0] = true;
for (int i = 1; i < size; i++)
{
lowcost[i] = VGraph[0][i];
closest[i] = 0;
visited[i] = false;
}
cout << "0";
int weight = 0;
for (int i = 0; i < size; i++)
{
int min = 99999;
int index = 1;
for (int j = 0; j < size; j++)
{
if (lowcost[j] < min&&!visited[j])
{
min = lowcost[j];
index = j;
}
}
if (index == 1 && min == 99999)
{
cout << "\n最小生成树权值为:" << weight<<endl;
return;
}
else
{
weight += min;
}
cout << " -> " << index;
visited[index] = true;
for (int j = 1; j <size; j++) //因为新加入了j点,所以要查找新加入的j点到未在S中的点K中的权值是不是可以因此更小
{
if ((VGraph[index][j]<lowcost[j]) && (!visited[j])) //lowcost表示从已知树到某一点所需付出的最小权值
{
lowcost[j] = VGraph[index][j];
closest[j] = index;
}
}
}
}
int main()
{
int M, N;
cin >>M>>N;
vector<vector<int>> VGraph(M);
vector<int> lowcost(M);
vector<int> closest(M);
vector<bool> visited(M);
for (int i = 0; i < M; i++)
{
VGraph[i].resize(M);
}
for (int i = 0; i < M; i++)
{
for (int j = 0; j < M; j++)
{
VGraph[i][j] = 99999;
}
}
for (int i = 0; i < N; i++)
{
int a, b;
cin >> a >> b;
int length;
cin >> length;
VGraph[a][b] = VGraph[b][a] = length;
}
prim(VGraph, lowcost, closest, visited);
}