第十二届蓝桥杯试题D:路径

试题D:路径


题目描述

​ 小蓝学习了最短路径之后特别高兴,他定义了一个特别的图,希望找到图中的最短路径。小蓝的图由2021 个结点组成,依次编号1 至2021。

对于两个不同的结点a, b,如果a 和b 的差的绝对值大于21,则两个结点之间没有边相连;如果a 和b 的差的绝对值小于等于21,则两个点之间有一条长度为a 和b 的最小公倍数的无向边相连
​ 例如:结点1 和结点23 之间没有边相连;结点3 和结点24 之间有一条无向边,长度为24;
结点15 和结点25 之间有一条无向边,长度为75。
​ 请计算,结点1 和结点2021 之间的最短路径长度是多少。
​ 提示:建议使用计算机编程解决问题。

解析:



一、前置知识

问题1:怎么求两个数a,b的最小公倍数呢?

答:【公式】 a , b 最小公倍数 = a × b / a , b 的最大公因数 a,b最小公倍数 = a \times b / a,b的最大公因数 a,b最小公倍数=a×b/a,b的最大公因数.

问题2:怎么求最大公因数呢?

答:辗转相除法

public static int gcd(int a,int b){
    return b==0?a:gcd(b,a%b);
}

问题3:阈值的选取

答:这一步很重要,下午调试了很久都是因为阈值没选好。阈值不能很大,因为这样可能会使扩展部分的加法查出long型范围;阈值不能太小,太小导致答案在阈值外面。

问题4:long型和Long型的区别

int转long型自然转换,int转Long型强制转换。




二、分析

1、数据分析

  • 0 < i 和 j 两个节点之间的差的绝对值 <22,distance[i][j] =a,b最小公倍数
  • i 和 j 两个节点之间的差的绝对值==0,distance[i][j] =0
  • i 和 j 两个节点之间的差的绝对值 > 21,distance[i][j] = 阈值,阈值十分重要,本次屡次在这犯错

2、过程分析

​ 在图中求一个点到另一个点的距离,可以使用Dijkstra和Floyd方法。可以使用Floyd方法是因为本题是填空题,时间复杂度不重要。

​ 关于Dijkstra和Floyd方法可以查看DijkstraFloyd




三、代码

1、Dijkstra代码

public class ExaminationD_Dijkstra {
    //求解最大公因数
    public static long gcd(long a,long b){
        return b==0?a:gcd(b,a%b);
    }

    //求解a,b的最小公倍数
    public static long lcm(long a,long b){
        return a*b/gcd(a,b);
    }

    public static long[] dijkstra(long[][] distance,int n,int origin){
        //辅助数组
        boolean[] used = new boolean[n];
        long[] minDis = new long[n];
        int pathIndex = origin-1;

        //初始化数组
        for (int i = 0; i < n; i++) {
            minDis[i] = distance[origin-1][i];
        }

        used[origin-1] = true;
        for (int i = 1; i < n; i++) {
            long min = Long.MAX_VALUE;
            //选取部分
            for (int j = 0; j < n; j++) {
                if (!used[j]&&minDis[j]<min) {
                    //System.out.println(String.format("变化前minDis[%d]=%d,min=%d",j,minDis[j],min));
                    min = minDis[j];
                    //System.out.println(String.format("变化后minDis[%d]=%d,min=%d",j,minDis[j],min));
                    pathIndex = j;
                }
            }
            used[pathIndex]= true;
            //扩展部分
            for (int k = 0; k < n; k++) {
                if (distance[pathIndex][k]!=Long.MAX_VALUE) {//选取合适的阈值使答案在这个范围又要使下面的加法不差过long范围
                    if (minDis[pathIndex]+distance[pathIndex][k]<minDis[k]) {
                        minDis[k] = minDis[pathIndex] + distance[pathIndex][k];
                        System.out.println(String.format("扩展minDisk[%d]=%d",k,minDis[k]));
                    }
                }


            }
        }

        return minDis;
    }

    public static void main(String[] args) {
        int n = 2021;
        long[][] dis = new long[n][n];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                if (Math.abs(i-j)<=21&&Math.abs(i-j)>0) {
                    dis[i][j] = dis[j][i] = lcm(i+1,j+1);
                }else if(i==j){
                    dis[i][j] = 0;//在扩展部分可能会加上去
                }else if(Math.abs(i-j)>21){
                    dis[i][j] = Long.MAX_VALUE;
                }
            }
        }
        long[] res = dijkstra(dis, n, 1);
        System.out.println(res[n-1]);

    }
}

2、Floyd代码

//麻烦的是怎么设定一个阈值
public class ExaminationD_Floyd {

    //求解最大公因数
    public static long gcd(long a,long b){
        return b==0?a:gcd(b,a%b);
    }

    //求解a,b的最小公倍数
    public static long lcm(long a,long b){
        return a*b/gcd(a,b);
    }

    public static long[][] floyd(long[][] distance,int n,int origin){
        for (int k = 0; k <n; k++) {
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < n; j++) {
                    if (distance[k][j]!=99999999L) {
                        //设定阈值使小于阈值的两个数相加不超过long范围
                        if (distance[i][k]+distance[k][j]<distance[i][j]) {
                            distance[i][j] = distance[i][k]+distance[k][j];
                        }
                    }

                }
            }
        }
        return distance;
    }
    public static void main(String[] args) {
        int n = 2021;
        long[][] dis = new long[n][n];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                if (Math.abs(i-j)<=21&&Math.abs(i-j)>0) {
                    dis[i][j] = dis[j][i] = lcm(i+1,j+1);
                }else if(i==j){
                    dis[i][j] = 0;
                }else if(Math.abs(i-j)>21){
                    dis[i][j] = 99999999L;
                }
            }
        }
        long res[][] = floyd(dis,n,1);
        System.out.println(res[0][n-1]);
    }
}




四、参考材料

蓝桥杯2021初赛 路径 Java

[2]2021蓝桥杯 java路径

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值