426.拨号器

60 阅读2分钟

代码思路分析

该题目旨在解决一个骑士拨号器问题,其中骑士在3x4矩阵上按照国际象棋中马的移动方式进行跳跃。目标是计算长度为n的所有不同电话号码的数量。

  1. 初始化

    • 定义了一个MOD变量,用于存储取模的值(10^9 + 7),以避免整数溢出。
    • 定义了一个dialer矩阵,虽然这个矩阵在后续代码中并未使用,但它表示了拨号器的布局。
    • 定义了一个moves字典,用于存储每个数字可以跳跃到的其他数字。
  2. 动态规划数组初始化

    • 使用一个二维数组dp来存储中间结果,其中dp[step][num]表示在第step步时,以数字num结尾的电话号码的数量。
    • 初始化dp数组的第一行,表示在第0步时,每个数字都可以作为起点,因此它们的数量都是1。
  3. 动态规划状态转移

    • 遍历每一步(从1到n-1)和每个数字(从0到9)。
    • 对于每个数字num,遍历它可以跳跃到的所有数字next_num
    • 更新dp[step][num]的值,将其增加dp[step-1][next_num]的值,表示在第step-1步以next_num结尾的电话号码数量可以跳跃到第step步以num结尾的电话号码。
    • dp[step][num]取模,以避免整数溢出。
  4. 计算最终结果

    • 遍历dp[n-1]数组,计算所有以任意数字结尾的电话号码的总数,并对结果取模。

改进方向

  1. 去除未使用的变量

    • dialer矩阵在代码中并未使用,可以将其删除。
  2. 优化空间复杂度

    • 由于只需要知道前一步和后一步的状态,可以使用一维数组代替二维数组来存储动态规划的结果,从而节省空间。
  3. 预处理输入

    • moves字典是固定的,可以将其定义为常量或在函数外部初始化,以避免在每次函数调用时都重新创建它。
  4. 代码简洁性

    • 可以进一步简化代码,例如通过合并循环和条件语句,以及使用更清晰的变量名。

代码如下

def solution(n: int) -> int:
MOD = 10**9 + 7

moves = {
    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],
    0: [4, 6]
}

# 初始化动态规划数组
dp = [[0] * 10 for _ in range(n)]

# 初始状态:第0步时,每个数字都可以作为起点
for num in range(10):
    dp[0][num] = 1

# 动态规划状态转移
for step in range(1, n):
    for num in range(10):
        for next_num in moves[num]:
            dp[step][num] += dp[step-1][next_num]
            dp[step][num] %= MOD

# 计算最终结果
result = sum(dp[n-1]) % MOD
return result