小F的棋子移动 | 豆包MarsCode AI 刷题

85 阅读3分钟

小F的棋子移动代码

    # 初始化 dp 数组,False 表示必败,True 表示必胜
    dp = [[False] * m for _ in range(n)]
    
    # 从右下角开始计算
    for i in range(n - 1, -1, -1):  # 从最后一行向上遍历
        for j in range(m - 1, -1, -1):  # 从最后一列向左遍历
            # 如果已经在棋盘的边界,不能再移动,则是必败状态
            if i == n - 1 and j == m - 1:
                dp[i][j] = False
                continue
            
            # 检查可以移动的方向
            # 向右移动:步数是奇数,1, 3, 5, ...
            for step in range(1, max(n, m), 2):  # 步长是奇数
                # 向右移动
                if j + step < m and not dp[i][j + step]:
                    dp[i][j] = True
                    break
                # 向上移动
                if i + step < n and not dp[i + step][j]:
                    dp[i][j] = True
                    break
    
    # 初始位置 (0, 0) 是否是必胜状态
    return "Yes" if dp[0][0] else "No"

# 测试代码
if __name__ == '__main__':
    print(solution(1, 1) == "No")  # 输出 No
    print(solution(1, 4) == "Yes")  # 输出 Yes
    print(solution(4, 1) == "Yes")  # 输出 Yes
    print(solution(4, 4) == "No")   # 输出 No

解题思路

问题分析

题目中我们可以知道棋盘的大小是 n×m,起始位置是左上角 (1,1),该位置表示为数组的索引 (0, 0)。根据规则每次小F可以选择向右或向上移动棋子,并且步数必须是奇数。此外移动到棋盘外的玩家也被判定为输掉游戏。我们需要判断小F是否能在初始位置必胜。该问题为博弈问题,那么在博弈问题中,我们需要判断每个位置是否是“必胜”状态。故可假设当前位置 (i,j):

  • 假设从 (i,j)位置出发,小F有任何一种有效的移动方式能让对方进入必败状态,那么当前状态就是必胜状态
  • 如果所有可能的移动都会让对方进入必胜状态,那么当前位置就是必败状态

步骤

  • 初始化:创建一个大小为 n×m 的 DP 表格,用来记录每个位置的状态。True 表示必胜状态,False 表示必败状态。

  • 边界条件:对于棋盘的右下角位置,如果当前无法移动,则是必败状态。

  • 状态转移:从右下角开始,向上和向左遍历每个位置,检查是否可以通过移动到另一个位置让对方进入必败状态。

  • 判断最终结果:最终我们要关注的就是起始位置 (0, 0)的状态

代码分析

  1. 初始化DP数组- 初始化 DP 数组

    dp = [[False] * m for _ in range(n)]
    

    创建一个 n×mn \times mn×m 的二维数组 dp,初始化为 False。表示每个位置的初始状态是必败状态

  • 从右下角向上遍历

    for i in range(n - 1, -1, -1): # 从最后一行向上遍历
        for j in range(m - 1, -1, -1):  # 从最后一列向左遍历
    

    从棋盘的右下角开始遍历,逐步推导每个位置的状态。

  • 边界条件

    if i == n - 1 and j == m - 1:
        dp[i][j] = False
        continue
    

    对于右下角的 (n-1, m-1) 位置,无法再移动,因此是必败状态

  • 检查可以向右和向上移动的步骤

    for step in range(1, max(n, m), 2):  # 步长是奇数
        # 向右移动
        if j + step < m and not dp[i][j + step]:
            dp[i][j] = True
            break
        # 向上移动
        if i + step < n and not dp[i + step][j]:
            dp[i][j] = True
            break
    

    对于当前位置 (i, j),我们检查是否能通过向右或向上移动进入对方的必败状态。步数必须是奇数

  • 返回最终结果

     return "Yes" if dp[0][0] else "No"
    

    最后返回起始位置 (0, 0) 的状态,若为 True 则说明小F可以必胜,返回 "Yes",否则返回 "No"

优化思考

是否可以通过优化步长的计算、空间的使用以及减少多余的计算等,在保持了代码的可读性同时,是否可以将这段代码的性能进行进一步的优化