P 算法与 K 算法

作者:Grey

原文地址:

博客园:P 算法与 K 算法

CSDN:P 算法与 K 算法

说明

P 算法和 K 算法主要用来解决最小生成树问题,即:不破坏连通性删掉某些边,使得整体的权重最小。

测评链接:牛客-最小生成树

K 算法

K 算法使用的核心数据结构是并查集,然后将边权值排序。

1)总是从权值最小的边开始考虑,依次考察权值依次变大的边

2)当前的边要么进入最小生成树的集合,要么丢弃

3)如果当前的边进入最小生成树的集合中不会形成环,就要当前边

4)如果当前的边进入最小生成树的集合中会形成环,就不要当前边

5)考察完所有边之后,最小生成树的集合也得到了

边存在小根堆里面,保证每次弹出的都是权重最小的值

点存在并查集中,每次加入一个边,就把两个边的点 union

完整代码如下



import java.util.Arrays;
import java.util.Comparator;
import java.util.Scanner; public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int m = in.nextInt();
int[][] graph = new int[m][3];
for (int i = 0; i < m; i++) {
// from
graph[i][0] = in.nextInt();
// to
graph[i][1] = in.nextInt();
// weight
graph[i][2] = in.nextInt();
}
System.out.println(k(graph, n));
in.close();
} // k算法生成最小生成树
public static int k(int[][] graph, int n) {
UnionFind uf = new UnionFind(n);
Arrays.sort(graph, Comparator.comparingInt(o -> o[2]));
int ans = 0;
for (int[] edge : graph) {
if (!uf.same(edge[0], edge[1])) {
uf.union(edge[0], edge[1]);
ans += edge[2];
}
}
return ans;
} public static class UnionFind {
private final int[] parent;
private final int[] size;
private final int[] help; public UnionFind(int n) {
parent = new int[n + 1];
size = new int[n + 1];
help = new int[n + 1];
for (int i = 1; i < n; i++) {
parent[i] = i;
size[i] = 1;
}
} public boolean same(int a, int b) {
return find(a) == find(b);
} private int find(int a) {
int index = 0;
while (a != parent[a]) {
help[index++] = a;
a = parent[a];
}
index--;
while (index > 0) {
parent[help[index--]] = a;
}
return a;
} public void union(int a, int b) {
int f1 = find(a);
int f2 = find(b);
if (f1 != f2) {
int size1 = size[f1];
int size2 = size[f2];
if (size1 > size2) {
parent[f2] = f1;
size[f2] = 0;
size[f1] = size1 + size2;
} else {
parent[f1] = f2;
size[f1] = 0;
size[f2] = size1 + size2;
}
}
}
}
}

P 算法

1)可以从任意节点出发来寻找最小生成树

2)某个点加入到被选取的点中后,解锁这个点出发的所有新的边

3)在所有解锁的边中选最小的边,然后看看这个边会不会形成环

4)如果会,不要当前边,继续考察剩下解锁的边中最小的边,重复3)

5)如果不会,要当前边,将该边的指向点加入到被选取的点中,重复2)

6)当所有点都被选取,最小生成树就得到了

完整代码如下

import java.util.*;

public class Main {

    public static Set<Edge> P(Graph graph) {
// 解锁的边进入小根堆
PriorityQueue<Edge> priorityQueue = new PriorityQueue<>(Comparator.comparingInt(o -> o.weight)); // 哪些点被解锁出来了
HashSet<Node> nodeSet = new HashSet<>();
Set<Edge> result = new HashSet<>(); // 依次挑选的的边在result里
for (Node node : graph.nodes.values()) { // 随便挑了一个点
// node 是开始点
if (!nodeSet.contains(node)) {
nodeSet.add(node);
for (Edge edge : node.edges) { // 由一个点,解锁所有相连的边
priorityQueue.add(edge);
}
while (!priorityQueue.isEmpty()) {
Edge edge = priorityQueue.poll(); // 弹出解锁的边中,最小的边
Node toNode = edge.to; // 可能的一个新的点
if (!nodeSet.contains(toNode)) { // 不含有的时候,就是新的点
nodeSet.add(toNode);
result.add(edge);
for (Edge nextEdge : toNode.edges) {
priorityQueue.add(nextEdge);
}
}
}
}
// 如果有森林,就不能break,如果没有森林,就可以break
//break;
}
return result;
} public static class Graph {
public HashMap<Integer, Node> nodes;
public HashSet<Edge> edges; public Graph(int n) {
nodes = new HashMap<>();
edges = new HashSet<>(n);
}
} public static class Node {
public int value;
public int in;
public int out;
public ArrayList<Node> nexts;
public ArrayList<Edge> edges; public Node(int value) {
this.value = value;
in = 0;
out = 0;
nexts = new ArrayList<>();
edges = new ArrayList<>();
}
} public static class Edge {
public int weight;
public Node from;
public Node to; public Edge(int weight, Node from, Node to) {
this.weight = weight;
this.from = from;
this.to = to;
}
} public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int m = in.nextInt();
Graph graph = new Graph(n);
for (int i = 0; i < m; i++) {
int from = in.nextInt();
int to = in.nextInt();
int weight = in.nextInt();
if (!graph.nodes.containsKey(from)) {
graph.nodes.put(from, new Node(from));
}
if (!graph.nodes.containsKey(to)) {
graph.nodes.put(to, new Node(to));
}
Node fromNode = graph.nodes.get(from);
Node toNode = graph.nodes.get(to);
Edge fromToEdge = new Edge(weight, fromNode, toNode);
Edge toFromEdge = new Edge(weight, toNode, fromNode);
fromNode.nexts.add(toNode);
fromNode.out++;
fromNode.in++;
toNode.out++;
toNode.in++;
fromNode.edges.add(fromToEdge);
toNode.edges.add(toFromEdge);
graph.edges.add(fromToEdge);
graph.edges.add(toFromEdge);
}
Set<Edge> result = P(graph); int sum = 0;
for (Edge edge : result) {
sum += edge.weight;
}
System.out.println(sum);
in.close();
}
}

更多

算法和数据结构笔记

参考资料

算法和数据结构体系班-左程云

最新文章

  1. 如何通过倾斜摄影数据手动配置s3c索引文件?
  2. c# 调用c++DLL方法及注意事项
  3. CSS中的绝对定位与相对定位
  4. 使用CSS3对链接颜色与下划线进行优化
  5. JSON之Asp.net MVC C#对象转JSON,DataTable转JSON,List转JSON,JSON转List,JSON转C#对象
  6. LCS(打印全路径) POJ 2264 Advanced Fruits
  7. AMPPZ2014
  8. [BIM]BIM中IFC介绍
  9. (转)mysql账号权限密码设置方法
  10. 海外支付:遍布全球的Paypal
  11. MediaPlayer开发全解析
  12. 雄冠条码PV系统-2016-05-17-收获
  13. Django ORM模型的一点体会
  14. Luogu 2245 星际导航(最小生成树,最近公共祖先LCA,并查集)
  15. JS的for循环小例子
  16. 厉害了!阿里安全图灵实验室在ICDAR2017 MLT竞赛刷新世界最好成绩
  17. 探索 Python 学习
  18. java_基础_接口和抽象类
  19. 2017年5月11日17:43:06 rabbitmq 消费者队列
  20. POJ 1724 ROADS(使用邻接表和优先队列的BFS求解最短路问题)

热门文章

  1. js中数组去重的方法
  2. Linux 01 概述
  3. Go 语言入门 3-动态数组(slice)的特性及实现原理
  4. Scrum五大会议要怎么开?
  5. CAP 6.2 版本发布通告
  6. Python实验报告——第4章 序列的应用
  7. nginx配置文件内容详解
  8. 最佳实践:4个黄金指标和USE方法
  9. Go 源码解读|如何用好 errors 库的 errors.Is() 与 errors.As() 方法
  10. vue中a标签地址传参