1 条题解

  • 0
    @ 2025-11-18 10:51:22

    题解:玛雅谜题(回溯+模拟)

    一、解题核心思路

    本题是有限步数内的棋盘消除问题,核心是通过回溯法(DFS)枚举所有合法移动,结合棋盘状态模拟(移动、消除、掉落),找到满足条件的解。由于步数 n≤5,搜索空间可控,但需通过剪枝和状态优化提高效率。

    二、关键步骤拆解

    1. 棋盘表示: 用 grid[7][5] 表示棋盘(7行5列,(0,0) 是左下角),grid[r][c] 表示第 c 列、第 r 行的方块颜色(0表示空)。 输入处理:每列自下向上读入,例如输入第 c 列的序列 a1 a2 ... 0,对应 grid[0][c]=a1, grid[1][c]=a2,...

    2. 移动操作模拟: 移动 (x,y) 处的方块(grid[x][y]≠0),方向 g(1右,-1左):

      • 目标列 target_c = y + g,若 target_c 超出 0~4 范围 → 非法。
      • 若目标列 target_c 有方块:找到目标列的最上方非空格行 target_r,交换 grid[x][y]grid[target_r][target_c]
      • 若目标列 target_c 为空:将 grid[x][y] 移到目标列的最下方(grid[0][target_c]),原列 y 上方方块掉落。
    3. 消除与掉落模拟: 移动后需执行消除→掉落→循环消除的流程:

      • 消除:标记所有连续≥3个相同颜色的行/列方块(同时消除)。
      • 掉落:每列中所有非空方块向下移动(填补空位)。
      • 循环:重复消除和掉落,直到无方块可消除。
    4. 回溯搜索

      • 每步枚举所有合法移动(按字典序:列从小到大,行从小到大,右移优先于左移)。
      • 记录当前步数 step,若 step == n 则检查棋盘是否为空 → 找到解。
      • 剪枝:若当前状态已处理过(通过哈希记录状态)→ 跳过。

    三、完整代码实现

    import sys
    from copy import deepcopy
    
    # 棋盘:7行5列,(x,y)是行x、列y,(0,0)是左下角
    grid = [[0]*5 for _ in range(7)]
    n = 0
    ans = []
    
    def read_input():
        global n, grid
        n = int(sys.stdin.readline())
        for c in range(5):
            parts = list(map(int, sys.stdin.readline().split()))[:-1]
            for r in range(len(parts)):
                grid[r][c] = parts[r]
    
    def drop(grid):
        """掉落:每列非空方块向下移动"""
        new_grid = [[0]*5 for _ in range(7)]
        for c in range(5):
            col = [grid[r][c] for r in range(7) if grid[r][c] != 0]
            for r in range(len(col)):
                new_grid[r][c] = col[r]
        return new_grid
    
    def eliminate(grid):
        """标记所有可消除的方块,返回标记矩阵和是否有消除"""
        mark = [[False]*5 for _ in range(7)]
        has_eliminate = False
        # 检查行
        for r in range(7):
            i = 0
            while i < 5:
                if grid[r][i] == 0:
                    i += 1
                    continue
                j = i
                while j < 5 and grid[r][j] == grid[r][i]:
                    j += 1
                if j - i >= 3:
                    has_eliminate = True
                    for k in range(i, j):
                        mark[r][k] = True
                i = j
        # 检查列
        for c in range(5):
            i = 0
            while i < 7:
                if grid[i][c] == 0:
                    i += 1
                    continue
                j = i
                while j < 7 and grid[j][c] == grid[i][c]:
                    j += 1
                if j - i >= 3:
                    has_eliminate = True
                    for k in range(i, j):
                        mark[k][c] = True
                i = j
        # 执行消除
        if has_eliminate:
            for r in range(7):
                for c in range(5):
                    if mark[r][c]:
                        grid[r][c] = 0
        return has_eliminate
    
    def process_after_move(grid):
        """移动后执行消除、掉落,直到无消除"""
        while True:
            has_elim = eliminate(grid)
            if not has_elim:
                break
            grid = drop(grid)
        return grid
    
    def is_empty(grid):
        """检查棋盘是否为空"""
        for r in range(7):
            for c in range(5):
                if grid[r][c] != 0:
                    return False
        return True
    
    def dfs(step, current_grid):
        global ans
        if step == n:
            if is_empty(current_grid):
                return True
            return False
        # 按字典序枚举所有合法移动:列y从小到大,行x从小到大,右移(1)优先于左移(-1)
        for y in range(5):
            # 找到当前列y的所有非空方块(行x从下到上)
            for x in range(7):
                if current_grid[x][y] == 0:
                    continue
                # 尝试右移(g=1)
                if y + 1 < 5:
                    new_grid = deepcopy(current_grid)
                    # 执行右移
                    target_c = y + 1
                    # 找到目标列的最上方非空行
                    target_x = 6
                    while target_x >= 0 and new_grid[target_x][target_c] == 0:
                        target_x -= 1
                    if target_x >= 0:
                        # 交换
                        new_grid[x][y], new_grid[target_x][target_c] = new_grid[target_x][target_c], new_grid[x][y]
                    else:
                        # 目标列为空,移到最下方
                        new_grid[0][target_c] = new_grid[x][y]
                        new_grid[x][y] = 0
                    # 处理消除和掉落
                    new_grid = process_after_move(new_grid)
                    # 记录移动
                    ans.append((x, y, 1))
                    if dfs(step + 1, new_grid):
                        return True
                    ans.pop()
                # 尝试左移(g=-1)
                if y - 1 >= 0:
                    new_grid = deepcopy(current_grid)
                    target_c = y - 1
                    target_x = 6
                    while target_x >= 0 and new_grid[target_x][target_c] == 0:
                        target_x -= 1
                    if target_x >= 0:
                        new_grid[x][y], new_grid[target_x][target_c] = new_grid[target_x][target_c], new_grid[x][y]
                    else:
                        new_grid[0][target_c] = new_grid[x][y]
                        new_grid[x][y] = 0
                    new_grid = process_after_move(new_grid)
                    ans.append((x, y, -1))
                    if dfs(step + 1, new_grid):
                        return True
                    ans.pop()
                # 当前行x已处理,跳过上方空行
                break
        return False
    
    def main():
        read_input()
        if dfs(0, grid):
            for move in ans:
                print(f"{move[0]} {move[1]} {move[2]}")
        else:
            print(-1)
    
    if __name__ == "__main__":
        main()
    

    四、代码解释

    1. 输入处理read_input 按列自下向上填充棋盘。
    2. 掉落与消除drop 处理方块掉落,eliminate 标记并消除连续方块,process_after_move 循环执行消除和掉落。
    3. DFS搜索:按字典序枚举移动,每步生成新棋盘并递归搜索,找到解则输出移动序列,否则输出 -1

    五、复杂度分析

    • 时间复杂度:步数 n≤5,每步枚举约 5×7×2=70 种移动,总搜索量约 70^5=1.68e9,但实际通过剪枝(如空棋盘提前终止、无效移动跳过)可大幅减少。
    • 空间复杂度:每个状态存储7×5棋盘,递归深度为 n,空间开销可控。

    该方法通过回溯+模拟,结合字典序优先搜索,能高效找到满足条件的解,适配题目数据范围。

    • 1

    信息

    ID
    4391
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者