以错位非空棋子数为估价函数,在深度 15 内做 IDA*。
OJ: luogu
题目 ID: P2324
难度:提高+/省选-
标签:IDA*启发式搜索棋盘python
日期: 2026-07-16 20:10
题意
空位与一个国际象棋骑士步位置交换,求 15 步内到目标棋盘的最少步数。
思路
逐步增加深度上限做 IDA*。一次移动只搬动一个非空棋子,所以当前与目标不同的非空棋子数,是到目标至少还需的步数;若它超过剩余深度立即剪枝。
搜索还禁止立刻把空位换回,并记录同一轮中某状态已拥有的最大剩余深度,避免以更差条件重复搜索。
Python 知识
- 棋盘保存在
bytearray中,递归时交换后再换回,避免大量复制。 seen.get(state, -1) >= remaining是带搜索资源的状态支配剪枝。- 目标和输入使用
bytes,*的字节值为 42。
代码
python
import sys
target = b"111110111100*110000100000"
moves = [tuple(nr * 5 + nc for dr, dc in ((1, 2), (1, -2), (-1, 2), (-1, -2),
(2, 1), (2, -1), (-2, 1), (-2, -1))
if 0 <= (nr := i // 5 + dr) < 5 and 0 <= (nc := i % 5 + dc) < 5)
for i in range(25)]
def solve(start):
board = bytearray(start)
blank = board.index(42)
def heuristic():
return sum(value != 42 and value != target[i] for i, value in enumerate(board))
def dfs(blank_position, previous_blank, remaining, seen):
estimate = heuristic()
if estimate == 0:
return True
if estimate > remaining:
return False
state = bytes(board)
if seen.get(state, -1) >= remaining:
return False
seen[state] = remaining
for other in moves[blank_position]:
if other == previous_blank:
continue
board[blank_position], board[other] = board[other], board[blank_position]
if dfs(other, blank_position, remaining - 1, seen):
return True
board[blank_position], board[other] = board[other], board[blank_position]
return False
for limit in range(16):
if dfs(blank, -1, limit, {}):
return limit
return -1
input = sys.stdin.buffer.readline
answers = []
for _ in range(int(input())):
answers.append(str(solve(b"".join(input().strip() for _ in range(5)))))
print("\n".join(answers))复杂度
最坏指数级,深度最多 15;空间为当前 DFS 路径和一轮判重表。
总结
IDA* 的关键是可证明不高估的下界;错位棋子数简单但已经足够有效。
:::
:::