洛谷: P3366 【模板】最小生成树
最小生成树解决问题: 如何用最小的代价,用n-1条边连接n个点的问题.
在最小生成树中,我们需要用到并查集中的 find 和 join。
思路: 求最短,自然考虑到贪心,首先选择最短的边, 次短……重复选择权值较小的边,直到选出了n-1条边,使n个点连通。
注意: 在选择的过程中必须排除掉会构成回路的边。
方案: 排序+贪心+并查集
算法流程:
1、将数据按权值排序,我们在离散的顶点中慢慢增加边,构造并查集 fa[n]。
2、选择权值最小的边(x1,y1),增加到图中。这条边的两个端点(x1,y1)被选中到最小生成树中,两个顶点彼此连通了,在并查集中合并(x1,y1)。
3、选择权值次小的边,首先判断这条边的增加是否构成回路,是否在同一集合:否 增加这条边,在并采集中合并(xi,yi);是 跳过这条边。
4、反复执行步骤3,直到选出n-1条边将n个点连通。
AC Code
可能是 edge 类的问题,不开启O2优化一直卡第9样例的内存, 开启O2优化后概率过
import java.util.*;
import static java.lang.System.out;
public class Main{
// 边
static class edge implements Comparable<edge>{
int f;
int t;
int c;
public edge(int f, int t, int c){
this.f = f;
this.t = t;
this.c = c;
}
public int compareTo(edge e) {
return this.c - e.c;
}
}
static int[] fa = new int[5007];
public static int findset(int x) {
if(x == fa[x]) return x;
return fa[x] = findset(fa[x]);
}
public static void union(int x, int y) {
int rootx = findset(x);
int rooty = findset(y);
if(rootx != rooty) fa[rootx] = rooty;
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int m = in.nextInt();
edge[] e = new edge[m];
for(int i = 0; i < m; i++) {
int a = in.nextInt();
int b = in.nextInt();
int c = in.nextInt();
// 1 <= n <= 5000
e[i] = new edge(a - 1, b - 1, c);
}
// 按权值从小到大排序
Arrays.sort(e);
// 初始化
for(int i = 0; i < n; i++) fa[i] = i;
int cnt = 0, ans = 0;
// 选 n - 1 条边将 n 个顶点连接起来就行
for(int i = 0; i < m; i++) {
// 已经是连通状态, 再连接就会成环
if(findset(e[i].f) == findset(e[i].t)) continue;
// 连通
union(e[i].f, e[i].t);
ans += e[i].c;
cnt++;
// 完成
if(cnt == n - 1) {
out.println(ans);
return ;
}
}
out.println("orz");
}
}