按字典序 DFS 枚举至多 5 步移动,完整模拟重力、同时消除与连锁反应。
OJ: luogu
题目 ID: P1312
难度:省选/NOI-
标签:DFS模拟剪枝python
日期: 2026-07-16 20:10
题意
在 5 列 7 行消除棋盘上执行恰好给定步数的横移,输出清空棋盘的字典序最小方案。
思路
搜索按 x、y、方向 1、-1 的顺序枚举,第一次找到的解自然字典序最小。交换后反复执行:列内压紧、标记所有横向或纵向三连、同时清零,直到不再消除。
向左与非空块交换,等价于较小 x 方块向右交换,前面已枚举过;因此左移只保留目标为空的情况。相同颜色交换不改变状态,也跳过。
若某种剩余颜色数量只有 1 或 2,它永远不能被三连消掉,可立即失败;相同 (步数, 棋盘) 的失败状态只搜索一次。
Python 知识
- 棋盘用 5 个长度 7 的列表表示,
[column[:] for ...]清楚复制分支状态。 Counter一行统计各颜色剩余数量。set.update适合汇总所有必须同时消除的坐标。map(lambda move: " ".join(map(str, move)), path)格式化三元操作序列。
代码
python
import sys
from collections import Counter
data = iter(map(int, sys.stdin.buffer.read().split()))
move_limit = next(data)
board = [[0] * 7 for _ in range(5)]
for column in range(5):
row = 0
while (color := next(data)):
board[column][row] = color
row += 1
def settle(state):
while True:
for column in range(5):
values = [color for color in state[column] if color]
state[column][:] = values + [0] * (7 - len(values))
marked = set()
for x in range(5):
for y in range(7):
color = state[x][y]
if not color:
continue
if x + 2 < 5 and state[x + 1][y] == state[x + 2][y] == color:
marked.update(((x, y), (x + 1, y), (x + 2, y)))
if y + 2 < 7 and state[x][y + 1] == state[x][y + 2] == color:
marked.update(((x, y), (x, y + 1), (x, y + 2)))
if not marked:
return
for x, y in marked:
state[x][y] = 0
def encode(state):
return tuple(value for column in state for value in column)
def is_empty(state):
return all(not color for column in state for color in column)
def viable(state):
return all(amount >= 3 for amount in Counter(
color for column in state for color in column if color
).values())
path = []
failed = set()
def dfs(step, state):
if step == move_limit:
return is_empty(state)
if is_empty(state) or not viable(state):
return False
key = step, encode(state)
if key in failed:
return False
for x in range(5):
for y in range(7):
if not state[x][y]:
continue
for direction in (1, -1):
target = x + direction
if not 0 <= target < 5:
continue
if direction == 1 and state[x][y] == state[target][y]:
continue
if direction == -1 and state[target][y] != 0:
continue
next_state = [column[:] for column in state]
next_state[x][y], next_state[target][y] = next_state[target][y], next_state[x][y]
settle(next_state)
path.append((x, y, direction))
if dfs(step + 1, next_state):
return True
path.pop()
failed.add(key)
return False
if dfs(0, board):
print("\n".join(map(lambda move: " ".join(map(str, move)), path)))
else:
print(-1)复杂度
搜索深度最多 5,最坏指数级;每次模拟只处理固定 35 格棋盘。
总结
本题的难点是模拟语义必须准确:先同时标记,再统一消除,重力后继续连锁,不能边扫描边删。


