最近在看PiL3,今天在做习题的时候遇见了八皇后问题,当时示例代码并没有仔细看,等到写的时候发现老是有问题。后来去查了下八皇后问题的描述,发现自己虽然知道这个问题很久了却没有真正理解过,真为自己汗颜。
八皇后问题
以下资料来自WikiPedia:
八皇后问题是一个以国际象棋为背景的问题:如何能够在8×8的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后?为了达到此目的,任两个皇后都不能处于同一条横行、纵行或斜线上。八皇后问题可以推广为更一般的n皇后摆放问题:这时棋盘的大小变为n×n,而皇后个数也变成n。当且仅当n = 1或n ≥ 4时问题有解。
一个简单的递归算法
这个算法来自于PiL3中,我在做习题的时候将其自己实现了一遍,思路是一样的,下面介绍一下。
首先定义了2个全局变量,N表示棋盘的大小,solved代表问题是否已解决。
printBoard用来打印当前棋盘,皇后用X表示,空格用-表示。
validPos的作用是判断当前棋盘上能否在n行pos列上放置新棋子。
addQueen是算法的核心函数,也是被递归调用的函数。board代表当前的棋盘,n表示当前放置棋子的行数。
当n大于N时表明当前棋盘已经有解了,直接打印即可。
当n小于N时,尝试当前行n上在pos列放置棋子,如果位置合适则在棋盘上记录该棋子,并继续在下一行添加新棋子(调用addQueen(board,n+1))。如果不合适,则舍弃当前棋盘,不再递归调用自身,我一开始没有仔细看这里,导致我没理解这个解法如何只对符合规则的棋盘继续求解。
总结:与最常用最容易想到的回溯法相比,递归的方法显得简单粗暴,不过代码也确实简单了不少。
Lua实现如下:
local N=8
local solved=false
local function printBoard(board)
if solved then
return
end
for i=1, N do
for j=1, N do
io.write(board[i]==j and "X" or "-", " ")
end
io.write("\n")
end
io.write("\n")
solved = true
end
local function validPos(board, row, pos)
for i=1, row-1 do
if (board[i] == pos) or
(row-i == math.abs(pos - board[i])) then
return false
end
end
return true
end
local function addQueen(board, n)
if n>N then
printBoard(board)
else
for pos=1, N do
if validPos(board, n, pos) then
board[n]=pos
addQueen(board, n+1)
end
end
end
end
addQueen({},1)