最小生成树问题:
一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。 [1] 最小生成树可以用kruskal(克鲁斯卡尔)算法或prim(普里姆)算法求出。prim是对点贪心,适用于稠密图;kruskal对边进行贪心,代码更简单更高效,但是处理边较多的图复杂度要差一些,所以适用于稀疏图。
Prim算法:
对点进行贪心操作。从任意一个点开始,把距离它最近的点v加入到T中;下一步把距离{u, v}最近的点w加入到T中;继续这个过程,直到所有点都 在T中。
#include<iostream>
#include<algorithm>
using namespace std;
const int inf = 0x3f3f3f3f;
const int num = 1001;
int n, m, ans;
int d[num];
int vis[num];//标记数组
int map[num][num];//存图
int prim(){
//初始化
for(int i=0; i<num; i++){ vis[i] = 0;d[i] = inf;}
ans = d[1] = 0;
for(int i=1; i<n; i++){
int x = 0;
for(int j=1; j<=n; j++)
if(!vis[j] && (x==0 || d[j]<d[x]))//从更新的边中找到不构成圈的最小边
x = j;
vis[x] = 1;
for(int y=1; y<=n; y++)//更新点x的边
if(!vis[y])
d[y] = min(d[y], map[x][y]);
}
for(int i=2; i<=n; i++)
if(d[i]==inf) return -1;//存在孤立点,不能存在最小生成树
else ans += d[i];
return ans;
}
int main(){
while(~scanf("%d%d", &n, &m)){
for(int i=0; i<=n; i++)
for(int j=0; j<=n; j++)
map[i][j] = inf;
for(int i=0; i<m; i++){
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
map[a][b] = map[b][a] = c;//存图
}
printf("%d\n", prim());
}
return 0;
}