AcWing 858. Prim算法求最小生成树
原题链接
简单
作者:
大海呀大海
,
2020-07-22 20:59:46
,
所有人可见
,
阅读 518
代码里面有我对算法的理解,和y总视频里的讲解
#include <iostream>
#include <cstring>
using namespace std;
const int N = 510, inf = 0x3f3f3f3f;
int n, m;//n为点, m为边
int g[N][N];//存入无向边
int dist[N];//表示当前的点到集合的距离
bool st[N];//这里的st表示是否已经加入到集合里面去
int prim(){
memset(dist, 0x3f, sizeof dist);//初始化,一开始的时候,所有的点都是距离集合为正无穷
int res = 0;
for (int i = 1; i <= n; i ++ ){
int t = -1; //t表示距离集合最短的点,刚开始的时候,通过下面的这两行代码会先变成1,即将1先存入集合
for (int j = 1; j <= n; j ++ )
if (!st[j] && (t == -1 || dist[t] > dist[j])) t = j;//找到距离集合最近的点,下面就将此点放入集合
//如果(1)j没有在集合内,(2)还没有找到距离集合距离最短的点,(3)当前的点到集合的距离大于j点
if (i != 1 && dist[t] == inf) return inf;
/*
这里有几点要解释的
第一,为什么i要 != 1, 因为当i等于1的时候,也就是我们刚开始循环的时候,我们并没有对1号点进行标记。
所以,我们在判断的时候,要先加上一个关于集合里是不是有了1号点的判断
(可以结合下面最近的一行代码注释的括号里看看)
第二,我们选则的这条边就算已经是最短的边了,也可能距离集合为正无穷,所以也要判断一下
*/
if ( i != 1 ) res += dist[t];
//如果当前这个点不是第一个点(因为第一个点已经在集合内了,如果放入1号点,就多算距离了。
///(集合中只有1号点,那么集合本身的res为0,因为一个点构不成边,只有至少两个点的时候,才存在边)
/*
这里要先累加后更新,因为题目里说,存在重边和自环,同时存在负权边
当我们用到下面的代码的时候,会出现g[t][t]的时候,这就是自环了,但是我们不需要自环
更不需要负权边的自环
*/
for (int j = 1; j <= n; j ++ ) dist[j] = min(dist[j], g[t][j]);
//这里我们要将所有距离集合的点的距离更新一下,用t来更新
st[t] = true;
//确定t这个点是距离集合最近的点,并且这个点已经在集合里面了,那么我们就把t标记一下
}
return res;
}
int main(){
scanf("%d%d", &n, &m);
memset(g, 0x3f, sizeof g);//将数据初始化,每一个点都是独立,都到其他的距离为inf,同样到集合距离也是inf
for (int i = 1; i <= m; i ++ ){
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
g[a][b] = g[b][a] = min(g[a][b], c);//淦掉重边
}
int t = prim();
if (t == inf) puts("impossible");//如果是inf说明没有通路
else printf("%d\n", t);
return 0;
}