最小生成树

最小生成树(采用Prim算法)

题目描述:设G =(V,E)是无向连通带权图,即一个网络。E中的每条边(v,w)的权为c[ v ] [ w ]。如果G的一个子图G’是一颗包含G的所有顶点的树,则称G’为G的生成树。生成树上各边权的总和称为该生成树的耗费。在G的所有生成树中,耗费最小的生成树称为G的最小生成树。

大白话:在一个无向图中,找到点数 - 1 个边可以连起来所有顶点,并且所有边权加起来最小。

下面以该题为例:
在这里插入图片描述

先看看Prim算法是如何进行描述,我们整体走一遍。从起点开始,选择未选进集合的最小的可达点。
在这里插入图片描述图一

在这里插入图片描述图二

在这里插入图片描述图三

在这里插入图片描述图四

在这里插入图片描述图五

贪心策略:如果我们自己去找最小生成树,以我自己为例,可能每次我都会下意识的去找 对应那一点可以走的最小的点,但是走到第三张图的时候,我就无法再走下去了,所以贪心策略不是以当前点为中心,找到他可达周围的最小点。正确的贪心策略应该是,从目前可达的点中,选择一个最小的点去到达。所以,我们需要不断的去更新可达点的最小花费。

对起点v0,将可达点花费进行更新
在这里插入图片描述

选择v4,将可达点花费进行更新。对应图一

在这里插入图片描述

选择v5,将可达点花费进行更新。对应图二
在这里插入图片描述

选择v1,将可达点花费进行更新。对应图三
在这里插入图片描述

选择v2,将可达点花费进行更新。对应图四

在这里插入图片描述

选择v3,将可达点花费进行更新。对应图五

在这里插入图片描述

整体代码如下

public class Prim {
    int [][] c; // 邻接矩阵
    int [] closest; //记录最优连线
    int [] lowcost; //记录目前可达点最小花费
    boolean [] s; //判断点是否被选进集合
    int n;//点集大小

    public Prim(int n,int [][] c){
        this.n = n;
        this.c = c;
        closest = new int[n + 1];
        lowcost = new int[n + 1];
        s = new boolean[n + 2];
        prim(n,c,s,closest,lowcost);
    }
    public static void main(String[] args) {
        int M = Integer.MAX_VALUE;
        int[][] c = {
                {0,34,M,M,12,M},
                {34,0,46,M,M,19},
                {M,46,0,17,M,25},
                {M,M,17,0,38,25},
                {12,M,M,38,0,26},
                {M,19,25,25,26,0}
        };
        int n = c.length - 1;
        new Prim(n,c);
    }
    public static void prim(int n, int [][] c,boolean [] s,int [] closest, int[] lowcost){
        int cost = 0;
        List<Integer> v = new ArrayList<>();
        s[1] = true; //初始点默认为true
        v.add(1);
        //初始化第一个点的最小花费
        for(int i = 1; i <= n; i++){
            s[i+1] = false;
            lowcost[i] = c[0][i]; //从 1 - i 的边权
            closest[i] = 1;
        }
       //System.out.println(Arrays.toString(lowcost));
        for(int i = 1; i <= n; i++){
            int min = Integer.MAX_VALUE;
            int j = 1;
            //遍历 k可达的点
            for(int k = 1; k <= n; k++){
                if(lowcost[k] < min  && !s[k + 1] ){
                    min = lowcost[k];
                    j = k;
                }
            }
//            System.out.println((j) + " 号点选择边:" + lowcost[j]);

            cost += lowcost[j];

            s[j + 1] = true;
            v.add(j+1);

            //更新可达的点
            for(int k = 1; k <= n; k++){
                if(c[j][k] < lowcost[k] && !s[k + 1]){
                    lowcost[k] = c[j][k];
                    closest[k] = j + 1;
                }
            }
//            System.out.println(Arrays.toString(lowcost));
        }

        //最小生成树点集
        System.out.println("最接近点集" + v);
        System.out.println("边集选择" + Arrays.toString(lowcost));
        System.out.println("最小花费:" + cost);
    }
}
/** 结果
最接近点集[1, 5, 6, 2, 3, 4]
边集选择[0, 19, 25, 17, 12, 26]
最小花费:99
*/
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值