这题是本人遇到的第一道动态规划的题目,在百度上找了很多资料后都不能理解(菜鸡)。写下我理解的过程,帮助其他新人一起进步。
先上题目:
算法训练 K好数
时间限制:1.0s 内存限制:256.0MB
问题描述
如果一个自然数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%的数据,K^L <= 106;
对于50%的数据,K <= 16, L <= 10;
对于100%的数据,1 <= K,L <= 100。
BB两句:
我看到题目的想的是把所有可能都穷尽一遍然后采用计数器记录符合要求的个数。然后在实现的过程中发现了许多问题,例如进制的转换,转换后的还原和原数不一样,如何判断大于10的数与周围数的关系。
在查阅了各种资料后才知道这是属于动态规划类的问题。
实现思路:
从第一位开始存储,以k进制中每一个数结尾的可能结果一共有多少种。接着第二位,在第一位的基础上以k进制的每一位结尾一共有多少种,一直到第L位。
例如:以题目中的例子 K=4,L=2。4进制,位数为2.
在4进制下,一共有0,1,2,3这4个数字
第一位以0结尾,有一种
以1结尾,有一种
以2结尾,有一种
以3结尾,有一种
即0,1,2,3这4个数
0 | 1 | 2 | 3 | |
---|---|---|---|---|
1 | 1 | 1 | 1 | 1 |
我们就有了如上表格。
接着第二位的结尾就是在第一位的基础上再次进行结尾数字的选择(0除外,因为01,02,03在例子中没有给出)。
具体过程如图,一共有12个数字,排除掉和上一位的尾数相邻的5个数字,一共有7个数字符合要求。
可以得到如下表格,具体解答如下:
即答案为:2+2+3=7
7个数字满足要求。
若扩展到K=4,L=3有如下表格:
代码实现:
采用一个二维数组开存储数据arr[L+1][K]
下标L+1列表示第几位数字(从1开始,舍弃0),下标K表示以什么数字结尾。
arr[2][2]存储的内容为二位数字并以2结尾且满足题目要求互不相邻的数字有多少个(2个)
初始化第一位(因为不论L位几位,第一位的每个结尾只有一种可能)
for(i=0;i<k;i++)
arr[1][i]=1;
从第二位开始,对数组进行赋值
for(i=2;i<L+1;i++)
for(j=0;j<K;j++)
arr[i][j]=___;
从零开始扫描,如果有数字满足不等于上一个数字的结尾将其数组的内容加到当前数组中
for(x=0;x<k;x++){
if(x!=j-1 && x!=j+1 ){
arr[i][j]+=arr[i-1][x];
}
}
输出的时候对1000000007取余即可
for(i=0;i<k;i++){
count+=arr[L][i];
count%=1000000007;
}
完整代码:
import java.util.Scanner;
public class k好数 {
public static void main(String[] args) {
// TODO Auto-generated method stub
int k,L,i,j,a,count=0;
Scanner reader=new Scanner(System.in);
k=reader.nextInt();L=reader.nextInt();
int arr[][]=new int[L+1][k];
for(i=0;i<k;i++)
arr[1][i]=1;
for(i=2;i<=L;i++) {
for(j=0;j<k;j++) {
for(a=0;a<k;a++) {
if(a!=j-1 && a!=j+1) {
arr[i][j]+=arr[i-1][a];
arr[i][j]%=1000000007;
}
}
}
}
for(i=1;i<k;i++) {
count+=arr[L][i];
count%=1000000007;
}
System.out.print(count);
}
}
这篇文章是参考:云笔记大佬的文章后完成