象棋骑士有一个独特的移动方式,它可以垂直移动两个方格,水平移动一个方格,或者水平移动两个方格,垂直移动一个方格(两者都形成一个 L 的形状)。
象棋骑士可能的移动方式如下图所示:
我们有一个象棋骑士和一个电话垫,如下所示,骑士只能站在一个数字单元格上(即蓝色单元格)。
给定一个整数 n,返回我们可以拨多少个长度为 n 的不同电话号码。
你可以将骑士放置在任何数字单元格上,然后你应该执行 n - 1 次移动来获得长度为 n 的号码。所有的跳跃应该是有效的骑士跳跃。
因为答案可能很大,所以输出答案模 109 + 7
.
思路
这个问题可以使用动态规划来解决。我们可以使用一个二维数组 dp[i][j] 来表示在第 j 步时,骑士位于数字键 i 的不同拨号方式数量。然后,我们根据骑士在数字键盘上的移动规则,递推地计算出 dp 数组的值。
解题方法
动态规划数组初始化: 我们初始化一个大小为 10xN 的二维数组 dp,其中 N 是给定的步数。初始化时,每个位置的值都为 1,表示在任意一步骑士都可以到达该数字键。
动态规划递推: 我们按照骑士在数字键盘上的移动规则,逐步更新 dp 数组的值。具体来说,对于每个数字键 i 和每一步 j,我们根据骑士的移动规则计算出从键 i 到达其他键的可能路径数量,并将结果保存在 dp[i][j] 中。
计算总的拨号方式数量: 最后,我们计算 dp 数组中最后一列的所有值的总和,即为所求的结果。
复杂度
时间复杂度:O(N),其中 N 是给定的步数。我们需要填充一个大小为 10xN 的二维数组,每个位置的填充都是常数时间的操作。
空间复杂度:O(N),我们需要使用一个大小为 10xN 的二维数组来存储动态规划的结果。
class Solution {
public:
int knightDialer(int n) {
const int MOD = 1e9 + 7;
vector<vector<long long>> dp(10,vector<long long>(n,1));
for(int j=1;j<n;j++)
{
dp[0][j] = (dp[6][j-1]+dp[4][j-1]) % MOD;
dp[1][j] = (dp[6][j-1]+dp[8][j-1]) % MOD;
dp[2][j] = (dp[7][j-1]+dp[9][j-1]) % MOD;
dp[3][j] = (dp[4][j-1]+dp[8][j-1]) % MOD;
dp[4][j] = (dp[9][j-1]+dp[3][j-1]+dp[0][j-1]) % MOD;
dp[5][j] = 0;
dp[6][j] = (dp[7][j-1]+dp[1][j-1]+dp[0][j-1]) % MOD;
dp[7][j] = (dp[2][j-1]+dp[6][j-1]) % MOD;
dp[8][j] = (dp[3][j-1]+dp[1][j-1]) % MOD;
dp[9][j] = (dp[4][j-1]+dp[2][j-1]) % MOD;
}
long long ret = 0;
for(int i=0;i<10;i++)
ret = (ret + dp[i][n-1]) % MOD;
return ret;
}
};
//dp[0][j] = dp[6][j-1]+dp[4][j-1];
//dp[1][j] = dp[6][j-1]+dp[8][j-1];
//dp[2][j] = dp[7][j-1]+dp[9][j-1];
//dp[3][j] = dp[4][j-1]+dp[8][j-1];
//dp[4][j] = dp[9][j-1]+dp[3][j-1]+dp[0][j-1];
//dp[5][j] = 0;
//dp[6][j] = dp[7][j-1]+dp[1][j-1]+dp[0][j-1];
//dp[7][j] = dp[2][j-1]+dp[6][j-1];
//dp[8][j] = dp[3][j-1]+dp[1][j-1];
//dp[9][j] = dp[4][j-1]+dp[2][j-1];
//dp[j] =