解答:
思路是先录入邻接矩阵,随机抽取一点开始(这里选取最后一个录入的边的起始点),把该点放入MST数组中,然后用双层循环遍历arr[MST[i]][j],j从1到节点数n,这样就遍历了所有已访问和未访问的节点中所有的边,将最小边的权值加到sum,并且把相连的节点放入MST数组 ,继续循环,得到最后的边权和。(这里没有说权值大于零,假定权值大于零)
#include<iostream>
using namespace std;
int main()
{
int i,j,k=0,a,b,n,m,index,visited[2001]={0},MST[2001];
//题目里面说节点数小于2000,但是开两千的数组会导致无法运行,经过测试,其实100大小的邻接矩阵就够了
long long arr[100][100]={0};
long long max,sum=0,w;
cin>>n>>m;
//如果这是一个没有边的图,则最小生成树的边权和为0
if(m==0)
{
cout<<0;
return 0;
}
//把权值录入邻接矩阵
for(i=0;i<m;i++)
{
cin>>a>>b>>w;
//自环不录入,重边选小的录入
if(a!=b&&(arr[a][b]==0||w<arr[a][b]))
{
//由于是无向图,所以邻接矩阵要录入两个方向的权值
arr[a][b]=w;
arr[b][a]=w;
}
}
//每次遍历一行
visited[a]=1;//把a添加到已访问
MST[k]=a;//已访问节点
k++;//k为已访问节点数
//在所有已访问的节点与未被访问的节点中找到一条最小的边
while(1)
{
max=9223372036854775807;
for(i=0;i<k;i++)
{
for(j=1;j<=n;j++)
{
if(!visited[j]&&arr[MST[i]][j]!=0&&arr[MST[i]][j]<max)//如果j没有被访问过,且小于max
{
max=arr[MST[i]][j];
index=j;//保留j
}
}
}
//循环结束后
visited[index]=1;//把index添加到已访问
MST[k]=index;//已访问节点
k++;//k为已访问节点数
sum=sum+max;
if(k==n) break;
}
cout<<sum;
}
QA:
Q1:什么是Prim算法
A1:
-
Prim 算法:
- 初始化一个集合
MST
,用于存储最小生成树的边。 - 选择一个起始顶点
start
。 - 将
start
标记为已访问,并将其加入MST
。 - 重复以下步骤,直到所有顶点都被访问:
- 在所有已访问的顶点和未访问的顶点之间的边中,选择权值最小的边
(u, v)
,其中u
已访问,v
未访问。 - 将顶点
v
标记为已访问,并将边(u, v)
加入MST
。
- 在所有已访问的顶点和未访问的顶点之间的边中,选择权值最小的边
- 遍历
MST
,累加其中的边的权值,即为最小生成树的边权和。
- 初始化一个集合
Q2:如何处理自环,重边
A2:
自环不需要考虑,因为最小生成树是无环图。
假设ab两点之间有权值为3,4两条边,则在以a为起点选择最小边时,只有可能选择权值为3的那一条,所以我们在录入邻接矩阵的时候只需要录入小的权值即可。
题目:
18448 最小生成树
时间限制:1000MS 代码长度限制:10KB
提交次数:0 通过次数:0
题型: 编程题 语言: G++;GCC;VC
Description
给定结点数为n,边数为m的带权无向连通图G,所有结点编号为1,2,3....n。
求图G的最小生成树的边权和。
输入格式
第一行两个正整数n和m。n,m<=2000
之后的m行,每行三个正整数a,b,w,描述一条连接结点a和b,边权为w的边。1=<a,b<=n,w<=10^18。
注意可能存在重边和自环。
输出格式
一个整数表示图G的最小生成树的边权和(注意用长整型)。
输入样例
7 12
1 2 9
1 5 2
1 6 3
2 3 5
2 6 7
3 4 6
3 7 3
4 5 6
4 7 2
5 6 3
5 7 6
6 7 1
输出样例
16
作者 30002692