图的开始--第八天打卡之最小生成树(Prim)
作者:
lgd123
,
2021-08-01 18:23:55
,
所有人可见
,
阅读 313
/*最小生成树
1、关于什么是最小生成树在我的《图的开始-连通分量》里有解释
2、对于一个图,它是由点和边构成的,所以在组建(寻找)树的时候,可以通过找点或找边的方法来求所需的树。
3、这里介绍两种常用的求最小生成树的方法:
补:对于连通网:一个连通图中,它的边上有权,则这个连通图叫连通网。
①、Prim算法:俗称“加点法”,通过选择的点,在所选的点的所有的边中,找出权最小的边,并选中与之相连的点
实现步骤:
设图的顶点集合为V,最小生成树中顶点集合为T
(1)从V中 随意选一点作为最小生成树的根节点,并将其加入到T中
(2)循环以下操作,直到V的所有顶点加入到T中
在连接T内的顶点与V-T内顶点的边中选择权值最小的边,将其作为最小生成树的边,加入到T中。
*/
----------
//这里的Prim算法使用 临街矩阵实现的,邻接矩阵的时间复杂度为:O(v^2)即为顶点个数的平方
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int WHITE = 0;//没选到的点
const int GRAY = 1;//预备点,下个选的点就可能是该点
const int BLACKE = 2;//若选中,则用黑色表示
const int N = 5010;
const int INFTY =1<<21;
int n,m[N][N],k;//m用来储存邻接矩阵
int l,r,e;
int Prim()
{
int u , minv;
int d[N],p[N],color[N];//color[]用来储存每一个访问的点的状态
for(int i = 0;i < n ; i ++)
{
d[i]=INFTY;
}//d[]用来记录T内顶点与V-T内顶点的边中,权值最小的边的权值
memset(p,-1,sizeof(p));//p[]用来记录最小生成树中的顶点V的父节点
d[0]=0;
while(1)
{
minv = INFTY;//用来记录上一个边的权值
u = -1;//
for(int i = 0 ; i < n ;i ++)
{
if(minv > d[i]&&color[i]!=BLACKE)
{
u = i;
minv = d[i];//更新权值
}
}
if(u==-1) break;
//寻找与u相连的边中,权值最小的边
color[u] = BLACKE;
for(int v = 0; v < n;v ++)
{
if(color[v]!=BLACKE&&m[u][v]!=0)
{
if(d[v]>m[u][v])
{
d[v] = m[u][v];
p[v] = u;
color[v] = GRAY;
}
}
}
}
int sum = 0;
//统计权值和
for(int i = 0;i<n;i++)
{
if(p[i]!=-1)
{
//cout<<"a";
sum+=m[i][p[i]];
//cout<<"sum = "<<sum<<" m[i][p[i]] = "<<m[i][p[i]]<<" i = "<<i<<" p[i] = "<<p[i]<<endl;
}
}
return sum;
}
int main()
{
scanf("%d %d",&n,&k);
memset(m,0,sizeof(0));
//判断重边,无向图的输入,两点之间权值一样
for(int i = 0 ;i < k ;i++)
{
scanf("%d %d %d",&l,&r,&e);
if(m[l-1][r-1]!=0)
{
m[l-1][r-1]=min(e,m[l-1][r-1]);
m[r-1][l-1]=m[l-1][r-1];
}else {
m[l-1][r-1]=e;
m[r-1][l-1]=e;
}
}
printf("%d",Prim());
return 0;
}