Prim算法求最小生成树 - 代码注释- 算法证明- golang
作者:
望哥
,
2020-07-16 10:52:23
,
所有人可见
,
阅读 849
package main
import (
"fmt"
"bufio"
"os"
)
const N=510 // 1≤n≤500
const M=100010 // 1≤m≤10^5 , 稠密图,邻接矩阵存储
const MaxDist = N*10000*2 // 图中涉及边的边权的绝对值均不超过10000
var (
reader=bufio.NewReader(os.Stdin)
writer=bufio.NewWriter(os.Stdout)
n,m int // 第一行包含两个整数n和m。
u,v,w int // 接下来m行,每行包含三个整数u,v,w,表示点u和点v之间存在一条权值为w的边。
g [N][N]int // 稠密图,邻接矩阵存储
dist [N]int // 节点和现有生成树节点集合的距离,这一点比较特殊!!
st[N]bool // 是否已加入生成树
)
// prim 返回最小生成树权值
//
// prim 算法:
// 1. 从其中一点开始,加入最小生成树
// 2. 从尚未加入树的节点中选择和已选中集合距离最小的点加入到生成树。如果其他点都不可达,则不存在最小生成树。
// 3. 重复第2步,直到所有点加入树;
//
// 为什么以上算法是正确的呢?
// 证明:
// 假设已经存在最小生成树G, 现在要加入一个新的节点x,
// 选择 x 离 G 最近的边 l 连接到 G 中的 b 点加入树,树权最小, 任然是最小生成树。
// G从一个节点开始,到最后加入所有节点,结果仍是最小生成树;
//
// 从不同的节点开始,最终也会生成一个最小生成树(权值相同),但结构可能会不同;
// 比如边的权值都是1,生成的树就会有很多种了。
func prim() int{
for i:=1;i<=n;i++{
dist[i] = MaxDist
}
res:=0
for i:=1;i<=n;i++{
t:=-1
// 1. 寻找离集合距离最小的点t
for j:=1;j<=n;j++{
if !st[j] && (t==-1 || dist[t] > dist[j]) {
t = j
}
}
// 非第一个点,且找不到关联边
if i>1 && dist[t] == MaxDist {
return -1
}
// 2. 将节点t加入集合,并统计权值
st[t] = true
if t>1{
// 从第二个点开始统计权值
res+= dist[t]
}
// 3. 新加入点t,更新一下其他所有点离集合的距离,以便下次循环查找
for j:=1;j<=n;j++{
if !st[j] && dist[j] > g[t][j]{
dist[j] = g[t][j]
}
}
}
return res
}
func main(){
fmt.Fscan(reader, &n, &m)
//初始化邻接矩阵
for i:=1;i<=n;i++{
g[1][i] = MaxDist
}
for i:=2;i<=n;i++{
copy(g[i][:], g[1][:])
}
for i:=0;i<m;i++{
fmt.Fscan(reader, &u,&v,&w)
if g[u][v] > w {
// 无向边, 即双向边
g[u][v] , g[v][u] = w,w
}
}
res:=prim()
// 共一行,若存在最小生成树,则输出一个整数,表示最小生成树的树边权重之和,如果最小生成树不存在则输出impossible。
if res==-1{
fmt.Println("impossible")
}else{
fmt.Println(res)
}
}