原题链接: https://leetcode.com/problems/knight-dialer/
1. 题目介绍
A chess knight can move as indicated in the chess diagram below:
国际象棋中,一个骑士可以按照下面的方法走:
This time, we place our chess knight on any numbered key of a phone pad , and the knight makes N-1 hops. Each hop must be from one key to another numbered key.
这次我们把这个骑士放到电话号码键盘上。最初,骑士在号码键盘的任意数字上。接下来他要走 N-1步,每一步必须从一个数字跳到另一个数字
Each time it lands on a key (including the initial placement of the knight), it presses the number of that key, pressing N digits total.
How many distinct numbers can you dial in this manner?
Since the answer may be large, output the answer modulo 10^9 + 7.
Note:
1 <= N <= 5000
骑士每次落到一个数字上时(包括他最初所在的数字),就会按下那个号码。一共会按出 N 位号码。
给出数字 N ,1 <= N <= 5000 ,代表骑士按出的号码位数。
问:骑士可以按出多少种不同的号码?
由于最后的结果可能会很大,因此答案对 109+ 7 取余。
2. 解题思路
注: 本解题思路参考了 https://leetcode.com/problems/knight-dialer/solution/
这个题我想了很久。动态规划最难的两个地方在于构造 dp 数组和推导递推关系式。而这个递推关系式我一直没有想出来。看了官方的题解后才恍然大悟。在这里我直接采用 LeetCode 题解中的思路吧。
我们可以设定一个函数 f ( start , n ) ,f ( start , n )代表了骑士从数字 start 开始,按下 n 位号码的不同走法的数量,这 n 位号码中的第一位就是数字start。
通过观察,可以发现:
从 0 出发,只能走到{4 , 6 },
从 1 出发,只能走到{6 , 8 },
从 2 出发,只能走到{7 , 9 },
从 3 出发,只能走到{4 , 8 },
从 4 出发,只能走到{3 , 9, 0 },
从 5 出发,哪里都去不了
从 6 出发,只能走到{1 , 7 ,0 },
从 7 出发,只能走到{2 , 6 },
从 8 出发,只能走到{1 , 3 },
从 9 出发,只能走到{2 , 4 }.
因此,可以据此得到 f( start , n )的递推关系式。
比如: f( 1 , n ) = f( 6 , n-1 ) + f( 8, n-1 ).
f( 4 , n ) = f( 3 , n-1 ) + f( 9, n-1 ) + f( 0, n-1 ).
于是,我们可以构造一个二维数组 dp[ ][ ] 用来存放 f( start , n )的值。不用每次用到 f( start , n )的值都去重新计算。
dp[ n ][ start ] 中存放 f( start , n )的值。(注意start 是列,n 是行)
最后的答案就是 f(0, N) + f(1, N) + … + f(9, N) 的和。
实现代码
class Solution {
public int knightDialer(int N) {
int [][] dp = new int[N+1][10];
//对于较大的数,可以用_分隔
int MOD = 1_000_000_007;
int [][] moves = {
{4 , 6 },//0
{6 , 8 },//1
{7 , 9 },//2
{4 , 8 },//3
{3 , 9, 0 },//4
{ },//5
{1 , 7 ,0 },//6
{2 , 6 },//7
{1 , 3 },//8
{2 , 4 },//9
};
//填充dp[1]这一行中的每个元素都是1
//意味着当 N = 1 的时候,只有1种走法:停在原地
//dp[0]这一行是没有用的,因为N不可能为0
Arrays.fill(dp[1], 1);
for(int i = 2 ; i<=N ; i ++ ) {
for(int j = 0 ; j<=9 ; j++) {
for(int k = 0 ; k< moves[j].length; k++) {
dp[i][j] += dp[i-1][ moves[j][k] ];
dp[i][j] %= MOD;
}
}
}
long ans = 0;
for(int i = 0;i<10;i++) {
ans += dp[N][i];
}
ans %= MOD;
return (int)ans;
}
}