八皇后问题
问题介绍
在一个 8×8 的国际象棋棋盘上摆放8个皇后,使她们任意两个相互不在对方的射程内。
参见百度百科,八皇后问题。
思路
有两个主要问题:
- 判断皇后是否安全
- 以某种顺序考虑所有情况
动态地看,新加入的皇后不会影响已有皇后的攻击性。换句话说,原来相安无事的两个皇后不会因为新加入的皇后而变得相互攻击,新的皇后也不会改变原来皇后的威慑区域。正是本问题的这个特性,让我们在加入新皇后时可以单独考虑各个原有皇后,依次检查其是否与新皇后发生冲突。并且,在添加新皇后之后也不需要重新检查原有皇后。
编程
Scheme 递归
(define empty-board '())
(define (adjoin-position new-row k rest-queens)
(cons new-row rest-queens))
(define (safe? k positions)
(define queen-row (car positions))
(define (iter delta rest-queens)
(if (null? rest-queens)
true
(if (or (= queen-row (car rest-queens))
(= (abs (- queen-row (car rest-queens))) delta))
false
(iter (+ delta 1) (cdr rest-queens)))))
(iter 1 (cdr positions)))
(define (queens board-size)
(define (queen-cols k)
(if (= k 0)
(list empty-board)
(filter
(lambda (positions) (safe? k positions))
(flatmap
(lambda (rest-of-queens)
(map (lambda (new-row)
(adjoin-position new-row k rest-of-queens))
(enum-interval 0 (- board-size 1))))
(queen-cols (- k 1))))))
(queen-cols board-size))
(length (queens 8))
> 92
Python
穷举
from itertools import *
def safe(new_queen, rest_queens):
for delta, queen in enumerate(rest_queens, 1):
if new_queen == queen or abs(new_queen - queen) == delta:
return False
return True
def all_safe(queens):
return all(safe(queens[i], queens[i + 1:]) for i in range(len(queens)))
results = []
for queens in product(range(8), repeat=8):
if all_safe(queens):
results += [queens]
len(results)
> 92
递归
class Solution:
def totalNQueens(self, n: int) -> int:
def starmap(func, items):
res = []
for item in items:
res += func(item)
return res
def safe(chessboard, i):
for col, chess in enumerate(chessboard):
if i == chess or abs(i - chess) == len(chessboard) - col:
return False
return True
def extend(chessboard, n):
res = []
for i in range(n):
if safe(chessboard, i):
res.append(chessboard + [i])
return res
def r(coln):
if coln == 0:
return [[i] for i in range(n)]
else:
return starmap(lambda case: extend(case, n), r(coln - 1))
return len(r(n - 1))
关于循环的顺序
递归解法中,务必将递归调用放在外层循环,让这个费时的调用只计算一次。