问题描述
如果一个自然数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的所有可能的组合数。
0 | 1 | 2 | 3 | |
1 | 0 | 1 | 2 | 3 |
2 |
20 30 | 11 31 |
22 |
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;
}