Hdu1863-畅通工程(最小生成树模板题)(Kruskal算法和Prim算法实现)

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);
            }
        }
    }
}

猜你喜欢

转载自blog.csdn.net/zxzxzx0119/article/details/81540155