蓝桥杯--算法训练 K好数

问题描述

如果一个自然数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。

 

思路

首先,我们先来看看样例。同样的,我们建立了一个DP数组,行表示数字的长度,列表示数字最后一位的状态。

因此,对于DP[I][J]来说,他表示长度为i,最后一位是j的所有可能的组合数。

 0123
10123
2

00

20

30

11

31

02

22

03

13

33

可以看到,红色的这些组合明显不符合长度的要求,因此我们需要避免在组合“0”的末尾继续添加数字。

需要注意的是,上面的表格只是我为了形象的说明问题,将具体的组合表示了出来,实际解题中,只需要计算个数即可。

观察这个表格,我们可以发现,如果不考虑非法的情况,大体上DP[I][J]为上一行所有个数之和,再排除题目要求的相邻的情况个数。而要计算所有个数,只要将L行所有个数加起来即可。

在这里,我想说明,很多大佬的最后一步的计算个数中,直接将第一列忽略,从第二列开始加和。我认为是因为第一列的情况(例如00,20,30)恰好就是非法情况的逆序(00,02,03)。

至此,我们已经大体上将思路整理完毕,但是,我们忽略了一些特殊的地方,比如,长度为1,1进制,2进制的个数为多少?

如果为1进制,答案为0;如果为2进制,答案为2(0或1);

使用我的思路似乎只有1,1为特例,而如果如很多人一样在加和的过程中忽略第一列,似乎第一行只有1进制的结果才正确。只不过OJ系统似乎只判断了1,1的情况。-.-||


#include <bits/stdc++.h>
using namespace std;

#define MOD 1000000007

int main(void) {
  int k, l;
  cin >> k >> l;
  //动态规划使用的数组,行下标为长度,列下标为末尾添加的数
  vector<int> dp[l + 1];

  for (int i = 0; i < k; i++)
    dp[1].push_back(1); //长度为1,并且末尾指定的情况下只有可能为1

  for (int i = 2; i <= l; i++) {
    for (int j = 0; j < k; j++) {
      dp[i].push_back(0);
      for (int m = 0; m < k; m++) {
        if (m == j - 1 || m == j + 1 || (i == 2 && m == 0))
          continue;
        dp[i][j] += dp[i - 1][m];
        dp[i][j] %= MOD;
      }
    }
  }

  int sum = 0;
  for (int i = 0; i < k; i++) {
    sum += dp[l][i];
    sum %= MOD;
  }

  if (k == 1 && l == 1) //特例,解释见上文
    cout << 0;
  else
    cout << sum;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值