蓝桥杯刷题——day6

蓝桥杯刷题------day6

题目一

题干

小明发现49很有趣,首先,它是个平方数。它可以拆分为4和9,拆分出来的部分也是平方数。169 也有这个性质,我们权且称它们为:拼接平方数。100 可拆分1,00,这有点勉强,我们规定,0,00,000 等都不算平方数。小明想:还有哪些数字是这样的呢?你的任务出现了:找到某个区间的所有拼接平方数。
输入: 两个正整数 a,b,其中(a<b<10的6次方)
输出: 若干行,每行一个正整数。表示所有的区间 [a,b] 中的拼接平方数,从小到大输出。
示例一:

输入:

169 10000

输出:

169

361

1225

1444

1681

3249

4225

4900

9025

题目链接: 拼接平方数

解题思路

这条题目很简单,首先我们从a遍历到b,在遍历的过程中先要满足他是一个平方数,如果是平方数,那么我们记录他的位数,然后再根据位数逐个划分为两个部分,在判断这两个部分是否是平方数。下面是完整代码:

代码

java 复制代码
import java.util.Scanner;
public class Main {
    public static boolean is_sqrt(int a) {
        if (a == 0){
            return false;
        }
        double num = Math.sqrt(a);
        return num == Math.floor(num);
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int a = scanner.nextInt();
        int b = scanner.nextInt();

        for (int i = a; i <= b; i++) {
            int tmp = i;
            if (is_sqrt(tmp)) {
                int num = 1;
                while (tmp / 10 != 0) {
                    num++;
                    tmp = tmp / 10;
                }
                while (num > 0) {
                    num--;
                    int ten = (int) Math.pow(10, num);
                    int x = i / ten;
                    int y = i % ten;
                    if (is_sqrt(x)&& is_sqrt(y)){
                        System.out.println(i);
                    }
                }
            }
        }
    }
}

is_sqrt函数是用于判断一个数是否是平方数,首先根据题意,0不是平方数,然后我们将这个数进行开平方,然后向下取值,如果向下取值和开平方数是一样的,那就证明该数是平方数,然后num用于记录该数的位数,x和y分别记录分开来的数字,如果同时满足x和y都是平方数,则打印出来。

题目二

题干

在一个排列中,一个折点是指排列中的一个元素,它同时小于两边的元素,或者同时大于两边的元素。对于一个1∼n 的排列,如果可以将这个排列中包含t个折点,则它称为一个t+1 单调排列。例如,排列 (1,4,2,3)是一个3单调排列,其中4和2都是折点,给定n和k,请问1∼n的所有排列中有多少个k单调排列?
输入: 输入一行包含两个整数n和k。
输出: 输出一个整数,表示答案。答案可能很大,你可需要输出满足条件的排列 数量除以123456 的余数即可。
示例一:

输入:

4 2

输出:

12

题目链接: 排列数

解题思路

这条题目可以说是相当的难了,刚拿到手可能想到的是暴力解法,可能完全无从下手,这时候我们就要想到用动态规划来解决了,我们先来理解一下这条题目的大致意思,我们来看这么一个图:

我们可以看到有两个折点(5和1),这个5和1将这个数列分为了三段(红-蓝-红标了出来),题目问我们我现在给你一个两个数(一个表示节点的个数,一个表示分为了几段),问有多少种方法。这个就是题目的大致意思了,那么如何解决呢?我们假设dp[i][j] 表示 1∼i 的排列中j单调序列的个数。我们需要分以下几种情况:
1.j为偶数,并且序列开始处上升状态,如图:

  • 当我们把i+1插入到两个"峰"的前后时,我们发现j没有发生改变,并且开始序列仍然保持上升的状态,我们发现如果有j个单调序列,那么就会有j/2个"峰",那么得到转移方程式:

dp[i+1][j] = dp[i+1][j] + dp[i][j] × j

  • 当我们把i+1插入到开头处时,开头序列就会从上升状态变为下降状态,同时单调序列从j变为j+1;同样的,当我们把i+1插入到末尾时,结尾序列就会从下降状态变为上升状态,同时单调序列从j变为j+1;因此转移方程式:

dp[i + 1][j + 1] = dp[i + 1][j + 1] + dp[i][j] × 2

  • 以上两种考虑完了,我们发现将n+1插入其他的位置,j个单调序列就会变为j+2,除去以上两种情况,剩下(i+1)−j−2 = i−j−1种情况,得到转移方程:

dp[i + 1][j + 2] = dp[i + 1][j + 2] + dp[i][j] * (i - j - 1)

2.j为偶数,并且序列开始处下降状态,如图:

  • 当我们把i+1插入到两个"峰"的前后时,我们发现j没有发生改变,并且开始序列仍然保持下降的状态,我们发现如果有j个单调序列,那么就会有(j/2)-1个"峰",同时我们又发现,如果将i+1插入开始位置和结束位置,他的j也不会发生变化,因此总共有2×[(j/2)-1]+2个情况,化简可得就是为j,那么得到转移方程式:

dp[i+1][j] = dp[i+1][j] + dp[i][j] × j

  • 当我们把i+1插入开始的第二个节点和结束的倒数第二个节点时,j个单调序列就会从j变为j+1,因此我们得到转移方程:

dp[i + 1][j + 1] = dp[i + 1][j + 1] + dp[i][j] × 2

  • 以上两种考虑完了,我们发现将n+1插入其他的位置,j个单调序列就会变为j+2,除去以上两种情况,剩下(i+1)−j−2 = i−j−1种情况,得到转移方程:

dp[i + 1][j + 2] = dp[i + 1][j + 2] + dp[i][j] * (i - j - 1)

3.j为奇数,并且序列开始处上升状态4.j为奇数,并且序列开始处下降状态这两种情况与上面的结果是一模一样的所以我们就不在赘述,因为这些状态转移方程是一摸一样的,我们就可以进行合并,这也是为什么求状态转移方程时,我们会在前面加上一个自身,例如dp[i+1][j] = dp[i+1][j] + dp[i][j] × j ,这个状态转移方程中我们是dp[i+1][j] = dp[i+1][j] + dp[i][j] × j,而不是dp[i+1][j] = dp[i][j] × j,就是这个原因。最后让我们看一下完整代码:

代码

java 复制代码
import java.util.Scanner;
public class Main {
    public static void main(String[] args) {
        int[][] dp = new int[505][505];
        int mod = 123456;
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int m = scanner.nextInt();
        if (n == 1) {
            if (m == 1) {
                System.out.println(1);
            } else {
                System.out.println(0);
            }
        }
        dp[2][1] = 2;
        for (int i = 2; i < n; i++) {
            for (int j = 1; j <= m; j++) {
                dp[i + 1][j] = (dp[i + 1][j] + dp[i][j] * j) % mod;
                dp[i + 1][j + 1] = (dp[i + 1][j + 1] + dp[i][j] * 2) % mod;
                dp[i + 1][j + 2] = (dp[i + 1][j + 2] + dp[i][j] * (i - j - 1)) % mod;
            }
        }
        System.out.println(dp[n][m] % mod);
    }
}

这条题目可以说是相当有难度,因此千万不要因为做不出来垂头丧气,也不怕各位笑话这条题目我看别人解析,自己理解就花了一个多小时。当然如果能自己做出来那真的是特别的厉害,可以说是将动态规划融会贯通了,如果有什么疑问或者问题,欢迎私信+评论,谢谢各位的点赞收藏。

相关推荐
互联网杂货铺4 小时前
单元测试总结
自动化测试·软件测试·python·测试工具·职场和发展·单元测试·测试用例
熬夜学编程的小王7 小时前
【优选算法篇】前缀和与哈希表的完美结合:掌握子数组问题的关键(下篇)
数据结构·c++·算法·前缀和·蓝桥杯
X在敲AI代码7 小时前
力扣刷题D1
算法·leetcode·职场和发展
m0_694938017 小时前
Leetcode打卡:最近的房间
算法·leetcode·职场和发展
Y编程小白7 小时前
Leetcode经典题11--加油站
算法·leetcode·职场和发展
@每日一练9 小时前
【蓝桥杯Day1】:LCR 018. 验证回文串,left++<right--?
职场和发展·蓝桥杯
靠谱杨9 小时前
CDA LEVEL 1新大纲2023添加的内容
大数据·数据仓库·面试·职场和发展·跳槽·etl·etl工程师
黑客老李13 小时前
面试经验分享 | 杭州某安全大厂渗透测试岗二面
大数据·服务器·数据库·经验分享·安全·面试·职场和发展
柒月的猫15 小时前
取数位(蓝桥杯2017年试题E)
职场和发展·蓝桥杯