【网格 dp】B006_LC_骑士拨号器(枚举上一次所在的位置)

一、Problem

国际象棋中的骑士可以按下图所示进行移动:
【网格 dp】B006_LC_骑士拨号器(枚举上一次所在的位置)_第1张图片
这一次,我们将 “骑士” 放在电话拨号盘的任意数字键(如上图所示)上,接下来,骑士将会跳 N-1 步。每一步必须是从一个数字键跳到另一个数字键。

每当它落在一个键上(包括骑士的初始位置),都会拨出键所对应的数字,总共按下 N 位数字。

你能用这种方式拨出多少个不同的号码?

因为答案可能很大,所以输出答案模 10^9 + 7。

输入:1
输出:10

提示:

1 <= N <= 5000

二、Solution

方法一:dp

由于起点不固定,我们只能枚举所有起点,由于下一个状态由上一个状态转移过来,所以我们需要知道每一个格子可以由哪些格子调过来,于是我们初始化一个二维数组 from

  • 定义状态
    • f [ i ] [ j ] f[i][j] f[i][j] 表示第 i i i 次跳跃到数字 j j j 的方案数
  • 思考初始化:
    • f [ 0 ] [ 0...10 ] = 1 f[0][0...10] = 1 f[0][0...10]=1
  • 思考状态转移方程
    • f [ i ] [ j ] + = f [ i − 1 ] [ l a s t ] f[i][j] += f[i-1][last] f[i][j]+=f[i1][last]
  • 思考输出 a c c u m u l a t e ( f [ N − 1 ] [ 0 : 10 ] ) accumulate(f[N-1][0:10]) accumulate(f[N1][0:10])
class Solution {
    final static int mod = (int) 1e9+7, from[][] = {{4,6}, {6,8}, {7,9}, {4,8}, {0,3,9}, {}, {1,7,0}, {2,6}, {1,3}, {2,4}};
    public int knightDialer(int N) {
        int f[][] = new int[N][10];
        for (int j = 0; j < 10; j++) f[0][j] = 1;

        for (int i = 1; i < N; i++)
        for (int j = 0; j < 10; j++)
        for (int last : from[j]) {
            f[i][j] = (f[i][j] + f[i-1][last]) % mod;
        }
        int ans = 0;
        for (int j = 0; j < 10; j++) {
            ans = (ans + f[N-1][j]) % mod;
        }
        return ans;
    }
}

复杂度分析

  • 时间复杂度: O ( N × k ) O(N × k) O(N×k),k 为一个较大的常数
  • 空间复杂度: O ( n ) O(n) O(n)

你可能感兴趣的:(#,网格,dp)