蓝桥杯-K好数(详解易懂)java

8 篇文章 0 订阅

蓝桥杯-K好数java

问题描述

如果一个自然数N的K进制表示中任意的相邻的两位都不是相邻的数字,那么我们就说这个数是K好数。求L位K进制数中K好数的数目。例如K = 4,L = 2的时候,所有K好数为11、13、20、22、30、31、33 共7个。由于这个数目很大,请你输出它对1000000007取模后的值。

输入格式

输入包含两个正整数,K和L。

输出格式

输出一个整数,表示答案对1000000007取模后的值。

样例输入

4 2

样例输出

7

数据规模与约定

对于30%的数据,KL <= 106;
对于50%的数据,K <= 16, L <= 10;
对于100%的数据,1 <= K,L <= 100。

一开始拿到这道题的时候,就按自己的想法直接解,最后死在了时间上,后面的数据几乎都全部超时,后来在网上看题解的时候,才知道要用动态规划,这个时候我还是没看出来怎么用动态规划,而且对网上的那些题解也是没领悟到,不明白为什么要这么做,后来想明白过后,才觉得是真的巧妙啊,但是他们是怎么想到的呢,一直是困扰我的地方,可能还是自己练的题太少了。我就将我理解到的写一下。

一:有没有发现一般用动态规划的题,都是不直接去处理数据,而是直接去处理结果,就比如这道题,我自己想的时候我是直接去处理那个数是不是K好数,写一个函数去处理那个数,定义一个数组去存储,与处理那个数据有关的数,而动态规划是直接定义了一个数组里面存储的就直接是我们需要的结果,而动态规划都会填那个二维数组,最后填完的表,我们就可以从中找到我们所需要的答案。

二:对于这个题的动态规划解法,我之前一直疑问,也是受我自己思路的影响,它为什么要一位一位的去判断是不是k好数,因为题是不是判断L位的数是不是K好数吗,你为什么要判断1位的时候,2位的时候…到L位的时候,这是我一直不得其解的地方,后来有一瞬间就突然明白了,结合上动态规划的原理,分解成一个一个的子问题,自底向上,解决问题,在这道题里就是,要判断L位的数是不是K好数,我先判断一位的时候,是不是K好数,然后在判断两位数的时候是不是K好数,在判断三位数是不是K好数的时候,我就只用判断第二位数和第三位数满不满足条件,同理,当L很大的时候,我也只用判断最后一位和倒数第二位满不满足条件,因为之前的我都判断了,并且把结果存下来了,如果满足的话,我把之前的结果提出来再加上现在的就是了。

三:定义d[i][j]表示的是:长度是i,结尾数字是j的K好数的个数。当i=1时,也就是长度是1时,所有的数字都是K好数,因为没有数字相邻的情况。但是因为开头数字是j,所以要注意第一个数字不能是0,所以赋值时从1开始,最大为k-1。动态规划转移方程为dp[i][j]=(dp[i][j]+dp[i-1][q])。其实就是从第一个数字开始,逐个找出后面的数字不能与前一个数字相邻的情况有多少种,直到i=l为止。最后将长度为l的以0,1,2……k-1结尾的K好数的个数加起来。

来再结合代码看看,要是想更深刻的理解,就自己画个表自己,跟着程序跑两个循环就明白了

代码:

import java.util.Scanner;
public class Main {
//k好数 dp
    final static int maxn = 100;
    final static int M = 1000000007;
    static int d[][] = new int[maxn + 5][maxn + 5];//d[i][j]表示将i位置的值置为j时的k好数的个数
    //由分析,其状态转移方程从最低+1位开始,d[i][j]= ∑d[i-1][r](r!=j+1&&r!=j-1&&0<=r<=k-1)
//最高位j!=0,需特殊处理
    //其初始条件为d[0][j]=1;不受约束
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int k, l;
        k = sc.nextInt();
        l = sc.nextInt();
        for (int j = 0; j < k; j++)
            d[0][j] = 1;//第0列的值与第一列的值有关,第一列的值,即第一位的时候肯定k好数,所以起点是1。
        for (int i = 1; i < l; i++)//i=0已经初始化过了
        {
            for (int j = 0; j < k; j++)//求第i个位置放置每一个值时d[i][j]的大小;
            {
                d[i][j] = 0;
                for (int r = 0; r < k; r++) {
                    if (r != j + 1 && r != j - 1)//状态转移表达式要满足的条件
                    {
                        d[i][j] += d[i - 1][r];
                        d[i][j] %= M;
                    }
                }
            }
        }
        int ans = 0;
        for (int j = 1; j < k; j++)//最后一位的情况不包括0
        {
            ans += d[l - 1][j];
            ans %= M;
        }
        System.out.println(ans);
    }
}

最后:

还是得多练题,多思考!

  • 8
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值