最小生成树| 青训营笔记

70 阅读2分钟

这是我参与「第四届青训营 」笔记创作活动的第8天\

概述

最小生成树问题研究的是无向图,主要为 Prim 和 Kruskal 两个算法。这两个算法都是贪心算法,符合全局最优包含局部最优的“最优性原理”。其中Prim以点的是否纳入有别于Kruskal的判断最短边是否纳入。

  • Prim演示:

image.png

  • Kruskal演示: image.png

因Kruskal的代码更易理解,故稍后的代码先上Kruskal。

切入口(例题)

L 城一共有 N 个小区。
小明是城市建设的规划者,他计划在城市修 M 条路,每修建一条路都要支付工人们相应的工钱(需要支付的工钱 = 路的长度)。

然而小明所拿到的经费并不够支付修建 M 条路的工钱,于是迫于无奈,他只能将计划改变为修建若干条路,使得 N 个小区之间两两联通。

小明希望尽量剩下更多的经费投入到别的项目中,因此请你通过程序帮他计算出完成计划所需的最低开销。
样例:

5 6   
1 2 2
1 3 7
1 4 6
2 3 1
3 4 3
3 5 2

输出:8\

Kruskal(JAVA)

import java.util.*;
import java.io.*;
class edge{
    int from,to,dis;
    public edge(int a,int b,int c){
        from=a;  to=b;  dis=c;
    }
}
public class Main{
    static StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
    static int read()throws IOException{
        in.nextToken();
        return(int)in.nval;
    }
    static int N = (int)2e6 + 10;
    static edge e[] = new edge[N];
    static int n,m,s[]=new int[N];
    static int find(int x){//查询并查集,返回x的根
        return s[x]==x?x:(s[x]=find(s[x])) ;
    }
    static long kruskal(){
        long ans = 0;
        for(int i = 1;i <= n;i ++)  s[i] = i;//并查集初始化
        Arrays.sort(e,1,m+1,new Comparator<edge>(){  //对边做排序
            public int compare(edge a,edge b){return a.dis - b.dis;}
        });
        for(int i = 1;i <= m;i ++){    //贪心:逐一加入每个边
            int x = find(e[i].from),y = find(e[i].to);
            if(x == y) continue;
            n--;
            ans += e[i].dis;
            s[x] = y;              //合并
        }
        return ans;
    }
    public static void main(String args[])throws IOException{
        n = read();
        m = read();
        for(int i = 1;i <= m;i++){            
            int x = read();
            int y = read();
            int z = read();
            e[i] = new edge(x,y,z);
        }
        long ans = kruskal();
        System.out.print(n==1?ans:"-1");
    }
}

Prim(JAVA)

import java.io.*;
import java.util.*;
public class Main {
    static int N,M;
    static List<int[]>[] edges=new ArrayList[100001];
    static PriorityQueue<int[]> pq=new PriorityQueue<>(new Comparator<int[]>() {
        public int compare(int[] o1, int[] o2) {return o1[1]-o2[1];    }
    });
    static int[] dis;
    static int[] vis;
    static long ans;
    static int MAX=Integer.MAX_VALUE;
    static BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
    static StreamTokenizer st=new StreamTokenizer(br);
    static int nextInt() throws IOException {
        st.nextToken();
        return (int)st.nval;
    }
    public static void main(String[] args) throws IOException {
            N=nextInt();
            M=nextInt();
            dis=new int[N+1];
            vis=new int[N+1];
            for (int j = 1; j <= N; j++) {
                if(edges[j]==null) {
                    edges[j]=new ArrayList<>();
                }else {
                    edges[j].clear();
                }
            }
            int a,b,c=0;
            for (int j = 1; j <= M; j++) {
                a=nextInt();
                b=nextInt();
                c=nextInt();
                edges[a].add(new int[] {b,c});
                edges[b].add(new int[] {a,c});
            }

            Arrays.fill(dis, MAX);
            prim();
            System.out.println(ans);
            Arrays.fill(vis,0);
            pq.clear();        
    }
    private static void prim() {
        int count = 0;
        ans = 0;
        pq.add(new int[] {1,0});
        dis[1]=0;
        while(!pq.isEmpty()) {
            int[] point=pq.poll();
            if (vis[point[0]] == 1) continue;
            vis[point[0]] = 1;
            if (point[1] != 0) {
                ans += point[1];
                count++;
            }
            for (int i = 0; i < edges[point[0]].size(); i++) {
                int[] next=edges[point[0]].get(i);
                if(next[1]<dis[next[0]]) {
                    dis[next[0]]=next[1];
                    pq.add(new int[] {next[0],dis[next[0]]});
                }
                
            }
        }
        if (count != N - 1) ans = -1;        
    }

}

回顾

Prim------点
Kruskal---边
故Prim更适合稠密图,Kruskal在边很多的的时候不如Prim。

参考资料: 蓝桥云课