适用情况:稠密图时适用(比如点数 n = 500 边数 m = 10000)
思想:将最小生成树看成是一个集合,最开始这个集合中的元素为0个,然后依次添加不同的点进来
算法步骤:
1. 首先将所有点与集合的距离初始化为无穷大
2. 随便先加入一个点进入到该集合
3. 然后更新其他所有点到该点的距离(即与该集合的距离)
4. 选取距离最近的点加入到该集合(如果最近的点都是正无穷,说明该点没有联通到集合,直接返回)
5. 同以上步骤,将新加入该点附近没有在集合中的点到集合的距离全部更新
6. 继续同上,直到全部点都加入到集合中为止
代码:
#include<iostream>
#include<cstring>
using namespace std;
const int N = 510;
int g[N][N],dist[N];
int res;
bool st[N];
int n,m;
void prim(){
memset(dist,0x3f,sizeof dist); // 初始化到集合的距离
dist[1] = 0;
for(int i = 1;i <= n;i ++ ){
int t = -1;
for(int j = 1;j <= n;j ++ ){
if(!st[j] && (t == -1 || dist[t] > dist[j]))
t = j;
}
if(dist[t] == 0x3f3f3f3f){ // 说明没有新的点可以更新,与集合不连通,直接返回
puts("impossible");
return;
}
st[t] = true;
res += dist[t];
for(int j = 1; j <= n;j ++ ){
dist[j] = min(dist[j],g[t][j]); // 已经加入集合的元素dist为多少都不影响结果了
}
}
cout<<res<<endl;
}
int main(){
memset(g,0x3f,sizeof g);
cin>>n>>m;
for(int i = 1;i <= m;i ++ ){
int a,b,c;
cin>>a>>b>>c;
g[a][b] = g[b][a] = min(g[a][b],c); // 这里是无向图,同时加上两条边的距离
}
prim();
return 0;
}
几个注意点:
1. 该题中存在自环,不能够根据dist[i] 中存在 0x3f3f3f3f 来判断是否能生成最小生成树
(以下代码存在问题,因为在更新边的时候,如果更新的是自己到自己的距离,那么如果没有与集合联通的中存在自环的话,会把自己更新变小,那么就不存在dist == 0x3f3f3f3f 的情况了)
事实上,这道题在把点加入到集合之后,该点dist的距离就不重要了,随便让他怎么更新
for(int i = 1;i <= n;i ++ ){
if(dist[i] == 0x3f3f3f3f){
puts("impossible");
return;
}
}