分析
克鲁斯卡尔算法
-
算法思想:将所有边按照边权最小到大排序,依次枚举所有的边,如果边的两个端点已经在同一个集合中(可连通),就continue,否则合并边的两个端点(将边权加入到答案中),直到有n-1条边参与合并为止。
-
最后如何判断这个生成的子图是不是树?
代码
#include<iostream>
#include<stdio.h>
#include<math.h>
#include<string>
#include<string.h>
#include<algorithm>
#include <vector>
using namespace std;
#define ll long long
#define sc(x) scanf("%d",&x)
#define scc(x, y) scanf("%d%d",&x,&y)
#define p(x) printf("%d\n",x)
#define m(x, y) (x+y)>>1
#define l(x) x<<1
#define r(x) x<<1|1
const int maxn = 1e5 + 6;
int fa[maxn];
int n;
struct Edge {
int from, to, cost;
bool operator<(const Edge &p) const {
return cost < p.cost;
}
} edge[maxn];
//找到x的掌门人
int find(int x) {
if (x == fa[x]) return x;
return fa[x] = find(fa[x]);//路径压缩
}
//合并x和y所在的门派
void unite(int x, int y) {
int fx = find(x);
int fy = find(y);
if (fx == fy) return;
fa[fx] = fy;
}
//查看x和y的门派是否相同
bool check(int x, int y) {
int fx = find(x);
int fy = find(y);
if (fx == fy) return true;
return false;
}
int main() {
int m, ans, cnt;
while (~sc(n) && n) {
sc(m);
//初始化
for (int i = 1; i <= n; i++) fa[i] = i;
ans = 0, cnt = 0;//ans为路程总和,cnt为连接的边的数量
//读入边
for (int i = 1; i <= m; i++) scanf("%d%d%d", &edge[i].from, &edge[i].to, &edge[i].cost);
//对边的权值排序
sort(edge + 1, edge + m + 1);
for (int i = 1; i <= m; i++) {
//若两点不在同一条线
if (!check(edge[i].from, edge[i].to)) {
ans += edge[i].cost;
cnt++;
unite(edge[i].from, edge[i].to);//合并
}
}
//检验,打印cost之和
if (cnt == n - 1) p(ans);
}
return 0;
}
题目
用多组数据,用空行隔开,每组数据第一行有两个数,n个点和m个边,接下来有m行,from,to,cost,最后以0结尾。
- 输入示例
1 0
2 3
1 2 37
2 1 17
1 2 68
3 7
1 2 19
2 3 11
3 1 7
1 3 5
2 3 89
3 1 91
1 2 32
5 7
1 2 5
2 3 7
2 4 8
4 5 11
3 5 10
1 5 6
4 2 12
0
- 输出示例
0
17
16
26