最小生成树问题是实际生产生活中十分重要的一类问题。假设需要在n个城市之间建立通信联络网,则连通n个城市只需要n-1条线路。这时,自然需要考虑这样一个问题,即如何在最节省经费的前提下建立这个通信网。
可以用连通网来表示n个城市以及n个城市之间可能设置的通信线路,其中网的顶点表示城市,边表示两个城市之间的线路,赋于边的权值表示相应的代价。对于n个顶点的连通网可以建立许多不同的生成树,每一棵生成树都可以是一个通信网。现在,需要选择一棵生成树,使总的耗费最小。这个问题就是构造连通网的最小代价生成树,简称最小生成树。一棵生成树的代价就是树上各边的代价之和。
那么如何构建满足以上条件的生成树?这篇日志先介绍其中一种常用的普里姆(Prim)算法。Prim算法构建最小生成树,简单来说就是在图中,从某一顶点出发,逐步构建,让一棵小树逐渐长大。用一个例子来说明更清晰点吧!首先看下面一张无向网图:
要构造这张图的最小生成树,首先,假设我们从V0顶点开始出发,也就是以V0为根结点开始建树,接着往外扩展,从与V0顶点相邻的顶点中找出权值最小的顶点,可以看到是V6,所以把V6和V0连接起来。
也就是把V6收录进了这棵最小生成树中了。接着继续,从当前树中顶点的邻接点中(也就是V0和V6的邻接点中,找出权值最小的顶点)。可以看到是V1,所以把V1也接入最小生成树中。
然后继续,到V2结点,也接入最小生成树中
因为不能形成回路,所以V2和V6之间不能连接(虽然权值最小,等于3)。V0和V1之间也不能连接。所以只能V6和V4连接
最后V4和V5连接,V4再和V6连接,就完成了这棵最小生成树的构建了。
最后就有
算法实现
代码实现
void MiniSpanTree_Prim(MGraph G)
{
int min,i,j,k;
int adjvex[MAXVEX]; //保存相关顶点下标
int lowcost[MAXVEX];//保存相关顶点间边的权值
lowcost[0] = 0; //初始化第一个权值为0,即v0加入生成树
//lowcost的值为0,在这里就是此下标的顶点已加入生成树
adjvex[0]=0; //初始化第一个顶点下标为0
for(i=1;i<G.numVertexes;i++){
{
lowcost[i]=G.arc[0][i]; //将v0顶点与之有边的权值存入数组。
adjvex[i]=0; //初始化都为v0的下标
}
for(i=1;i<G.numVertexes;i++)
{
min = INFINITY; //初始化最小权值为∞
j=1;k=0;
while(j<G.numVertexes)
{
//循环全部顶点
if(lowcost[j]!=0 &&lowcost[j]<min)
{
min =lowcost[i]; //让当前权值成为最小值
k=j; //将当前最小值的下标存入k
}
j++;
}
//printf(“%d,%d”,adjvex[k],k); //打印当前顶点边中权值最小的边
cout<<adjvex[k]<<"-->"<<k<<endl;
lowcost[k] = 0; //将当前顶点权值 设置成0表示此顶点已完成任务
for(j=1;j<G.numVertexes;j++)
{
if(lowcost[j]!=0 && G.arc[k][j]<lowcost[j]){
lowcost[j] =G.arc[k][j];
adjvex[j] = k;}
}
}
}
上题
输入
输入的第一行包含一个正整数n,表示图中共有n个顶点。其中n不超过50。
以后的n行中每行有n个用空格隔开的整数,对于第i行的第j个整数,如果不为0,则表示第i个顶点和第j个顶点有直接连接且代价为相应的值,0表示没有直接连接。当i和j相等的时候,保证对应的整数为0。
输入保证邻接矩阵为对称矩阵,即输入的图一定是无向图,且保证图中只有一个连通分量。
输出
只有一个整数,即最小生成树的总代价。请注意行尾输出换行。
样例输入
4 0 2 4 0 2 0 3 5 4 3 0 1 0 5 1 0
样例输出
6
代码展示:
#include<iostream>
#define max 50
using namespace std;
int n,num=0;
int a[max][max];
void minitree(int a[max][max],int n){
int min,i,j,k;
int adjvex[max]; //保存相关顶点下标
int lowcost[max];//保存相关顶点间边的权值
lowcost[0]=0;
adjvex[0]=0;
for(i=1;i<n;i++){
lowcost[i]=a[0][i];//将和v0有关的点存入权值数组中
adjvex[i]=0;//初始化都是v0
}
for(i=1;i<n;i++){
min=1000;
j=1,k=0;
while(j<n){
if(lowcost[j]!=0&&lowcost[j]<min){
min=lowcost[j];
k=min;
}
j++;
}
num+=min;
lowcost[k]=0;//表明k进入最小生成堆。
for(j=0;j<n;j++){
//在这个更新的过程中就是寻找最小的过程
if(lowcost[j]!=0&&a[k][j]<lowcost[i]){
lowcost[j]=a[k][j];
adjvex[j]=k;
}
}
}
}
int main(int argc, char const *argv[]) {
cin>>n;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
cin>>a[i][j];
}
}
//建立邻接矩阵完成
minitree(a,n);
cout<<num<<endl;
return 0;
}