图的两个基本元素是点和边,因此,可以用两种方法构造最小生成树,prim算法与kruskal算法,均是基于贪心法,即全局最优一定包含局部最优,prim算法的原理是“最近的邻居一定在MST上”,kruskal算法的原理是“最短的边一定在MST上”.
prim算法
- 对点进行贪心操作,从任意一个点u开始,把距离它最近的点v加入到MST中;
- 把距离{u, v}最近的点w加入到MST中;
- 继续这个过程,直到所有的点都在MST中. 其思路与朴素版dijkstra算法相似。
const int N = 510, INF = 0x3f3f3f3f;
int g[N][N]; // 存储图
int dis[N], st[N]; //dis存储图中节点到MST的最短距离,st表示节点是否在MST上
void prim(){
memset(dis, 0x3f, sizeof dis); // 将所有节点到MST的距离初始化为无穷大
dis[1] = 0; st[1] = 1; //将节点1放入MST中
int ans = 0; //记录最小生成树上边的权重
for (int i = 0; i < n; i ++ ){
int t = -1;
for (int j = 1; j <= n; j ++ ){
if (!st[j] && (t == -1 || dis[j] < dis[t])) t = j; // 找到距离MST最近的节点
}
if (dis[t] == INF) { // 若节点t到MST的最短距离为无穷大,则表示图不连通,不存在最小生成树
puts("impossible");
return;
}
ans += dis[t];
st[t] = 1; //标记节点t加入MST
for (int j = 1; j <= n; j ++ ){
if (!st[j]){
dis[j] = min(dis[j], dis[t] + g[t][j]); //用节点t更新其他节点到MST的距离
}
}
}
cout << ans << endl;
}
int main(){
cin >> n >> m; //n为节点数, m为边数
int a, b, c;
memset(dis, 0x3f, sizeof dis);
for (int i = 0; i < m; i ++ ){
cin >> a >> b >> c; //输入m条边的信息
if (a == b) continue;
g[a][b] = g[b][a] = min(g[a][b], c); //对于重边情况,取最小值作为边的权重
}
prim();
}
kruskal算法
- 对边进行贪心操作,先将所有边按权值升序排序
- 从最短的边开始,把它加入MST中(使用并查集实现)
- 在剩下的边中找最短的边,判断其两端的节点是否在MST中,若有一个以上不在,则将其加入MST中
- 重复此步骤,直到所有节点都在MST中
const int N = 1e5 + 10, M = 2 * N;
int p[N], cnt, n, m; // p[N]表示节点所属的集合,当所有节点所属集合一致时,最小生成树构建完成,cnt表示生成树中的边的数目,cnt = n - 1时,表示构建完成
struct Edge{
int a, b, c;
}edge[M]; //稀疏矩阵存边
int find(int x) {return x == p[x] ? x : p[x] = find(p[x]);} //并查集中的查找操作
bool cmp(Edge A, Edge B){
return A.c < B.c; //自定义排序规则,按边权升序排序
}
void kruskal(){
int ans = 0;
for (int i = 0; i < m; i ++ ){
int a = edge[i].a, b = edge[i].b, c = edge[i].c;
int pa = find(a), pb = find(b); //找到节点a与b所属的集合pa与pb
if (pa != pb){ //若pa与pb不同,则将两个集合合并,并把该条边加入最小生成树中
p[pb] = pa; //并查集中的合并操作
cnt ++;
ans += c;
}
}
if (cnt == n - 1) cout << ans << endl; //若最小生成树中边的数量不为n - 1则代表不存在最小生成树
else puts("impossible");
}
int main(){
int a, b, c;
cin >> n >> m;
for (int i = 0; i < m; i ++ ){
cin >> a >> b >> c;
edge[i] = {a, b, c};
}
sort(edge, edge + m, cmp); //对所有边按权值升序排序
kruskal();
}