//最小生产成树prim算法
//两者区别:Prim在稠密图中比Kruskal优,在稀疏图中比Kruskal劣。Prim是以更新过的节点的连边找最小值,Kruskal是直接将边排序。
#define inf 123456789
#define maxn 5005
#define maxm 200005
//边采用链式前向星加边
struct Edge
{
int to;//表示从这个表头 head[from] 所到达的边
int w;//这条边所对应的权值
int next;//指向head[from] 中最后一个表头在Edge数组中的位置
}edge[maxm<<1];//注意这个存储方式同时存储了方向,无向图要存储两倍(相互)
int head[maxn];//接邻表表头
int shortEdge[maxn];//记录已经加入点的集合到未加入点集合的最小距离(权值)
int cnt=0;//输入计数器
int result=0;//结果
int n,m;//n个点,m条边
int now=1;//从第一条边开始进行最小生成树prim算法
int total=0;//点总数
bool vit[maxn];//记录哪些点已经加入集合
//输入,读入数据的函数
void add(int from,int to,int w)
{
edge[++cnt].to=to;
edge[cnt].w=w;
edge[cnt].next=head[from];
head[from]=cnt;//更新表头所指向的最后一个元素
}
//读入数据
void init()
{
scanf("%d%d",&n,&m);
for(int i=1,f,t,w;i<=m;i++)
{
scanf("%d%d%d",&f,&t,&w);
//注意无向图加两次,两点之间相互加
add(f,t,w);add(t,f,w);
}
}
//prim算法
int prim()
{
//先把shortEdge数组初始化为最大值
for(int i=2;i<=n;i++)
{
shortEdge[i]=inf;
}
//确认先从哪个点开始就把那个点的一行数据先加进来shortEdge数组进行初始化
//这里默认从第一个点开始
//计算出最小生成树集合到其它点集合距离的最小值,来初始化
for(int i=head[1];i;i=edge[i].next)
{
shortEdge[edge[i].to]=min(shortEdge[edge[i].to],edge[i].w);
}
//初始化完开始继续连续不断加入集合到剩下点的最短边
while(++total<n)
{
int temp=inf;
int now1=now;
//找出集合到剩下点之间的最小值
vit[now]=true;//表及此点已经加入集合
for(int i=1;i<=n;i++)
{
//判断此点是否已经加入点集
if(!vit[i]&&shortEdge[i]<temp)
{
temp=shortEdge[i];
now=i;//找到到剩下点集合的最短路径,就记录下点的标号
}
}
result+=temp;//统计边的长度
//加入最短边以后,更新shortEdge数组的值,到剩下点集的最短边
//因为之前已经计算过已加入点集到剩下点的最短路径值,所以只需要拿新加入点到剩下点集的路径值和原来进行比较并更新即可
for(int i=head[now];i;i=edge[i].next)
{
int temp1=edge[i].to;
//如果新加入的点到这个点的距离更小且这个点不在最小生成树内就更新
if(shortEdge[temp1]>edge[i].w&&!vit[temp1])
{
shortEdge[temp1]=edge[i].w;
}
}
if(now1==now) return 0;//如果找不到点了就返回输出
}
return result;
}
int main()
{
init();
prim();
//判断是否符合最小生成树条件
if(total==n) cout << result;
else cout << "orz";
return 0;
}
最小生成树 prim算法
最新推荐文章于 2024-07-14 12:25:25 发布