【算法练习】蓝桥杯训练一:区间k大数查询、最大最小公倍数、k好数

本文详细介绍了三种算法问题的解决方案:一是区间k大数查询,通过排序模拟找出指定区间内第k大的数;二是最大最小公倍数,探讨了如何找到三个数的最大公倍数,特别是涉及数论中的互质概念;三是k好数,解释了如何计算k进制下没有相邻相同数字的自然数个数。每个问题都结合了具体的Java代码实现进行说明。
摘要由CSDN通过智能技术生成

一、区间k大数查询(简单)(模拟)

在这里插入图片描述
在这里插入图片描述
直接模拟就可以,个人觉得题目不严谨,没有说重复元素,也没有说重复元素算并列还是算新的最大数。

取出L - R的数,排序,找第k大。

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt(); // 序列长度
        int[] arr = new int[n];
        for (int i = 0; i < n; i++) {
            arr[i]= sc.nextInt();  // 序列中的数
        }
        int m = sc.nextInt(); //表示询问的个数
        int[] res = new int[m]; // 存储询问的结果
        for (int i= 0; i < m; i++) {
            int l = sc.nextInt();
            int r = sc.nextInt();
            int k = sc.nextInt();

            int[] helper = new int[r - l + 1]; // 存 l - j 区间的数
            for (int j = 0; j < r - l + 1; j++) {
                helper[j] = arr[l + j - 1]; // 将 l , j 区间的数取出存入新数组
            }
            Arrays.sort(helper); // 对数组进行排序
            res[i] = helper[helper.length - k]; // 得到l , k 区间中第 k 大的数
        }
        // 遍历res数组,输出每次询问序列的结果
        for(int i = 0 ; i < res.length; i++) {
            System.out.println(res[i]);
        }
    }
}

二、最大最小公倍数(中等)(数论)

在这里插入图片描述
害怕这种数论题…,一开始想法是求连续三个数的gcd,如果gcd=1,就直接返回三个数的乘积,显然这是不对的,例如:2 6 7,最小公倍数应该是42,而不是2 * 6 * 7。

不应该是求三个数的gcd,而是两个两个数的gcd=1,保证它们两两互质。接下来先说两个结论:大于1的两个相邻的自然数必定互质。 两个数的最小公倍数在最大的情况就是当两个数互质的时候,他们的最小公倍数就是这两个数的乘积(当然也可以推广到n个数)。

而对于1~N的范围,肯定是 n*(n-1)*(n-2)的乘积最大、如果这三个数还两两互质的话那就最棒了。

那么n可能是奇数、偶数:

如果n是奇数,那么 n、n-1、n-2必定两两互质,要是有些纠结的话,那么我们就分析在什么情况下可能会存在公因子。n是奇数,那么n,n-1,n-2一定是两奇加一偶的情况。 公因子2直接pass,因为只有一个偶数。剩下n,n-2,就算n是3的倍数,由于n - 2与n只差了2,还没差到3,所以肯定是互质的,n,n-2只可能有1个是3的倍数。

如果n是偶数,继续分析n * (n-1) * (n-2),例如:8 7 6,这样的话n和n-2必定有公因子2,最后的最大最小公倍数=三数之积 / 公因子,那么就换成式子n * (n-1) * (n-3)变成:偶*奇*奇,变动最小的数,最大的数保持不变。这不就变成上面的情况了,但是需要考虑n 和 n - 3之间,n是偶数且是3的倍数是(如6 12),那么n - 3肯定是3的倍数。这样就存在公因子3。所以,如果n不是3的倍数,是可行的,但如果n是3的倍数,该怎么办?

如果n是3的倍数,我们再试着调整乘积式,第三个数调整肯定没用了,因为调整为n - 4,如果n是4的倍数,n - 4也是4的倍数,那么还是存在公因子4。调整第二个数也没用呀,错在n和n-3,不关n-1的事,所以只能调整n,调整为n - 2即可。最后转换为了n-2 n-1 n-3(这是在n是偶数,且为3的倍数的情况下。

还需要考虑只有两个数的情况,1 2,显然就只能1 * 2 * 1。

有了上面分析就可以写出代码:

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        long n = scan.nextLong();
        long ans = 0;
        if (n <= 2) {
            ans = n;
        } else if (n % 2 == 1) {
            // n是奇数的情况
            ans = n * (n - 1) * (n - 2);
        } else {
            // n是偶数,要看n是不是3的倍数
            if (n % 3 != 0) {
                ans = n * (n - 1) * (n - 3);
            } else {
                ans = (n - 1) * (n - 2) * (n - 3);
            }
        }
        System.out.println(ans);
    }
}

遇到输入规模大,试试找数学规律。

三、k好数(DP)

在这里插入图片描述
读了半天题目,一直以为这个k好数是自然数n,结果是自然数n的k进制表达!

1、当k = 4,L = 1时:
1位数,那就只能是0、1、2、3,注意题目中说的任意的相邻的两位,是指n的k进制表达中某一个数自己内在的关系。而不是指十进制的1位数!!!!

2、当k=4,L = 2时:
当第一个数字取1,那第二个数字只能取1、3(0、2都不能取,因为与1相邻,不能相邻但是可以相同),即:11、13
当第一个数字取2,那第二个数字只能取0、2,即:20、22
当第一个数字取3,那第二个数字只能取0、1、3,即:30、31、33

细品一下,当取1个数字时,就是看k进制数一位能够表达的范围,如果k=4,就可以取0-3,如果k=11,就可以取0-10,11个数。

当取2个数字时,只需要把k进制数的1位数的范围中与第1个数相邻的去掉,剩下的数的个数就是可能的组成,例如:k=4,第一个数取1,与其相邻的是0、2,k=4的1位数范围0-3,只剩下1、3,所以是11、13。

当取3个数呢? k还是=4,先确定第一个数=1,第二个数只能取1、3,如果取1,第三个数只能取1、3,如果取3,第三个数只能取0、1、3,结果为,L=2时,第一个数字取1 + 第二个数字取3的和 = 2 + 3

再确定第一个数=2,第二个数就只能取0、2,如果取0,第三个数只能取0、2、3,如果取2,第三个数只能取0、1、3。结果为,L=2时,第一个数字取0 + 第二个数字取2的和 = 3 + 2(L=2时,第一个数字取0还需要单独记录一下)

再确定第一个数=3,第二个数只能取0、1、3,取0,第三个数只能取0、2、3,如果取1,第三个数只能取1、3,如果取3,第三个数只能取0、1、3,结果为:3 + 2 + 3

dp[i][j],取 i 个数时,第1个数为 j 的k好数个数,dp[3][1] = dp[2][1] + dp[2][3](1、3取决于dp[3][1]的1,与其相邻的都不能取),注意L=1时,第一个数可以取0,但L >= 2时,第一个数不能取0。

把图画出来就可以很容易看出重叠部分。
在这里插入图片描述

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int K = scan.nextInt();
        int L = scan.nextInt();
        long[][] dp = new long[L + 1][K];
        // dp[i][j]: 取i个数,第1个数是j的k好数个数
        for (int i = 0; i < K; i++) {
            dp[1][i] = 1;
        }
        long sum = 0L;
        if (L == 1) {
            // 一位数的话,1进制数应该是可以取0的,个人认为答案应该是1
            // L >= 2,1进制数的k好数肯定=0,因为第一位不能取0
            if (K == 1) {
                sum = 0;
            } else {
                sum = K;
            }
        } else {
            // 外面遍历位数
            for (int i = 2; i <= L; i++) {
                // 遍历第一个数字
                for (int j = 0; j < K; j++) {
                    for (int k = 0; k < K; k++) {
                        // 去除相邻的情况
                        if (k != j + 1 && k != j - 1) {
                            dp[i][j] += dp[i - 1][k];
                            dp[i][j] %= 1000000007;
                        }
                    }
                }
            }
            // L > 1时,第一位不能从0算起,所以如果当k=1,也是满足的
            for (int i = 1; i < K; i++) {
                sum = (sum + dp[L][i]) % 1000000007;
            }
        }
        System.out.println(sum);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@u@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值