Hdu1863-畅通工程(最小生成树模板题)(Kruskal算法和Prim算法实现)
- Kruskal算法思想及流程
- Prim算法思想及流程:
题目链接
题意以及解析
就是一个求最小生成树的模板题;
Kruskal算法思想及流程:
- 首先各个顶点看成一个集合,每个顶点的根就是自己;
- 从整个图中边的集合中取出最小的一条(一开始对边的集合排序),判断该边的两个定点是不是同一个集合,如果不是,合并两个集合;
- 如果是,舍弃,继续取下一条边;
- 直到集合中有n - 1条边为止;
时间复杂度为为O(e^2), 使用并查集优化后复杂度为 O(eloge),与网中的边数有关,适用于求边稀疏的网的最小生成树
import java.io.BufferedInputStream;
import java.util.*;
public class Kruskal { //提交时改成Main
private static class Node{
public int value;
ArrayList<Node> nexts;
public Node(int value) {
this.value = value;
nexts = new ArrayList<Node>();
}
}
private static class Edge{
public int weight;
public Node from;
public Node to;
public Edge( Node from, Node to,int weight) {
this.from = from;
this.to = to;
this.weight = weight;
}
}
private static class Graph{
public HashMap<Integer,Node>nodes;
public HashSet<Edge> edges;
public Graph() {
nodes = new HashMap<Integer, Node>();
edges = new HashSet<Edge>();
}
}
//并查集结构
private static class UnionSet{
public HashMap<Node,Node>faMap;
public HashMap<Node,Integer>sizeMap;
public UnionSet() {
faMap = new HashMap<Node,Node>();
sizeMap = new HashMap<Node,Integer>();
}
//初始化
public void init(Collection<Node>nodes){
faMap.clear(); sizeMap.clear();
for(Node node : nodes){
faMap.put(node,node);
sizeMap.put(node,1);
}
}
public Node findHead(Node v){
Node fa = faMap.get(v);
if(fa != v){
fa = findHead(fa);
}
faMap.put(v,fa); //v的父改为根(沿途所有的)
return fa;
}
public boolean isSameSet(Node a,Node b){
return findHead(a) == findHead(b);
}
public void union(Node a,Node b){
if(a == null || b == null)return;
Node aF = findHead(a);
Node bF = findHead(b);
if(aF == bF)return;
int aSize = sizeMap.get(a);
int bSize = sizeMap.get(b);
if(aSize >= bSize){
faMap.put(bF,aF); //把bF挂到aF下面
sizeMap.put(aF,aSize + bSize);
}else {
faMap.put(aF,bF); //把aF挂到bF下面
sizeMap.put(bF,aSize + bSize);
}
}
}
//在优先级队列中按照边的权值升序排列
private static class EdgeComparator implements Comparator<Edge>{
@Override
public int compare(Edge o1, Edge o2) { //按照边的权重升序排列
return o1.weight - o2.weight;
}
}
public static Set<Edge> kruskal(Graph graph){
UnionSet unionSet = new UnionSet();
unionSet.init(graph.nodes.values()); //初始化
PriorityQueue<Edge>priorityQueue = new PriorityQueue<Edge>(new EdgeComparator());
for(Edge edge : graph.edges){
priorityQueue.add(edge);
}
HashSet<Edge>set = new HashSet<Edge>(); //保存这n-1条边
int cnt = 0;
while(!priorityQueue.isEmpty()){
Edge poll = priorityQueue.poll();
if(!unionSet.isSameSet(poll.from,poll.to)){
set.add(poll);
cnt++;
unionSet.union(poll.from,poll.to);
}
if(cnt == graph.nodes.size() - 1)break;
}
return set;
}
public static void main(String[] args) {
Scanner cin = new Scanner(new BufferedInputStream(System.in));
while(cin.hasNext()){
int m = cin.nextInt();
int n = cin.nextInt();
if(m == 0)break;
Graph G = new Graph();
for(int i = 1; i <= n; i++)G.nodes.put(i,new Node(i));
for(int i = 0; i < m; i++){
int from = cin.nextInt();
int to = cin.nextInt();
int w = cin.nextInt();
G.edges.add(new Edge(G.nodes.get(from),G.nodes.get(to),w));
}
Set<Edge> set = kruskal(G);
if(set.size() != n-1){ //没有n-1条边 不足以保持畅通
System.out.println("?");
}else {
int sum = 0;
for (Edge edge : set) {
sum += edge.weight;
}
System.out.println(sum);
}
}
}
}
Prim算法思想及流程:
- 一开始也有一个集合,和Kruskal算法不同的是,这个不是慢慢的合并变大,而是一个一个的添加结点;
- 一开始选择一个起点,有一个优先队列存放边的集合,把这个结点相连的边加入优先队列;
- 然后选择一条相连的且权值最小的边,并判断这条边的终点是否已经加入过点的集合,如果没有,就加入,并且把这条边加入到结果集,并且解锁和它相连的边(解锁就是把边加入到优先队列)
- 如果出现过,继续从优先队列中拿出最小的边判断;
- 知道结果集达到n-1条边,或者图不连通;
import java.io.BufferedInputStream;
import java.util.*;
public class Main{
private static class Node{
public int value;
ArrayList<Edge>edges; //以这个点作为起点出发的边
public Node(int value) {
this.value = value;
edges = new ArrayList<Edge>();
}
}
private static class Edge{
public int weight;
public Node from;
public Node to;
public Edge( Node from, Node to,int weight) {
this.from = from;
this.to = to;
this.weight = weight;
}
}
private static class Graph{
public HashMap<Integer,Node> nodes;
public HashSet<Edge> edges;
public Graph() {
nodes = new HashMap<Integer, Node>();
edges = new HashSet<Edge>();
}
}
private static class EdgeComparator implements Comparator<Edge>{
@Override
public int compare(Edge o1, Edge o2) { //按照边的权重升序排列
return o1.weight - o2.weight;
}
}
private static Set<Edge> prim(Graph graph){
PriorityQueue<Edge>priorityQueue = new PriorityQueue<>(new EdgeComparator());
HashSet<Edge>res = new HashSet<Edge>();
HashSet<Node>set = new HashSet<Node>();
Node start = graph.nodes.get(1) ; //从第一个点开始
set.add(start);
for(Edge edge : start.edges){
priorityQueue.add(edge);
}
while(!priorityQueue.isEmpty()){
Edge poll = priorityQueue.poll();
Node toNode = poll.to; //这条边的to点
if(!set.contains(toNode)){
set.add(toNode);
res.add(poll); //注意这个不能放在if的上面
if(res.size() == graph.nodes.size() - 1)break;
for(Edge nextEdge : toNode.edges){
priorityQueue.add(nextEdge);
}
}
}
return res;
}
private static Graph createGraph(Scanner cin,int n,int m){
Graph G = new Graph();
for(int i = 1; i <= n; i++)G.nodes.put(i,new Node(i));
for(int i = 0; i < m; i++){
int a = cin.nextInt();
int b = cin.nextInt();
int w = cin.nextInt();
Node from = G.nodes.get(a);
Node to = G.nodes.get(b);
Edge newEdge = new Edge(from,to,w);
G.edges.add(newEdge);
from.edges.add(newEdge); //记得添加这条边
}
return G;
}
public static void main(String[] args) {
Scanner cin = new Scanner(new BufferedInputStream(System.in));
while(cin.hasNext()){
int m = cin.nextInt();
int n = cin.nextInt();
if(m == 0)break;
Graph G = createGraph(cin,n,m);
Set<Edge> set = prim(G);
if(set.size() != n-1){ //没有n-1条边 不足以保持畅通
System.out.println("?");
}else {
int sum = 0;
for (Edge edge : set) {
sum += edge.weight;
}
System.out.println(sum);
}
}
}
}