题解 | #Sudoku# 标准回溯 Python3
逐个检查所有的零位置,当发现不满足时,尽早丢弃当前的部分解(分支)即可。所有的待验证部分解由深度优先的序列Q来管理遍历。
import enum
import sys
from collections import deque
# 处理输入
board = list(map(lambda line: list(map(int, line.split(" "))), sys.stdin.readlines()))
# 统计哪些位置需要求解
blank_pos = []
blank_pos2l = {}
L = 0
for irow, row in enumerate(board):
for icol, char in enumerate(row):
if char == 0:
blank_pos.append((irow, icol))
blank_pos2l[(irow, icol)] = L
L += 1
# 根据当前解和题面查询某位置的当前值
def lookupB(sln, pos):
m, n = pos
return board[m][n] or (sln[blank_pos2l[(m, n)]] if blank_pos2l[(m, n)] < len(sln) else 0)
# 3x3 格内坐标转换
def i2mn(m, n, i):
return (m // 3 * 3+ i // 3, n // 3 * 3 + i % 3)
# 根据当前部分解sln, 检查数字num是否适合填入位置pos
def check(num, pos, sln):
m, n = pos
count_row = sum(1 for i in range(9) if lookupB(sln, (m, i)) == num)
if count_row > 1: return False
count_col = sum(1 for i in range(9) if lookupB(sln, (i, n)) == num)
if count_col > 1: return False
count_blk = sum(1 for i in range(9) if lookupB(sln, i2mn(m, n, i)) == num)
if count_blk > 1: return False
return True
# 回溯 / DFS (等价)
Q = deque()
Q.append([])
sln = []
while Q:
sln = Q.pop()
l = len(sln)
if l == L:
break
for nxt in range(1, 10):
nxt_sln = sln + [nxt]
if check(nxt, blank_pos[l], nxt_sln):
Q.append(nxt_sln)
# 输出结果
for irow, row in enumerate(board):
for icol, char in enumerate(row):
print(
lookupB(sln, (irow, icol)),
end=" " if icol < 8 else ("\n" if irow < 8 else ""),
)
查看21道真题和解析