试探算法思想:先暂时放弃关于问题规模大小的限制,并将候选解按照某一顺序逐一枚举和检验
算法步骤:
- 针对所给问题定义解空间
- 确定易于搜索的解空间结构
- 以深度优先方式搜索解空间,并在搜索过程中用剪枝函数避免无效搜索
例题:
八皇后问题:在8*8的国际象棋上摆放8个皇后,使其不能相互攻击,即任意两个皇后都不能处在同一行同一列或同一斜线上,问有多少种解法:
本题思路:
- 确定解空间:使用x来存储一条解空间,由于每行(或每列)都有且只能有一个皇后,因此存储时使用一个一维列表即可存储解,即x = [x0,x1,x2,x3,x4,x5,x6,x7],其中
xi
的值代表第i
行中的第多少列。all_x
则代表所有的可行解。 - 定义一个confict(k)函数,该函数通过可以检测前k项是否冲突,定义一个queen(k)函数,该函数通过递归,通过深度优先搜索算法,从一行开始由小到大遍历,并在遍历过程中使用confict(k)函数检测是否有冲突,进行剪枝操作。
n = 8
x = [] # 一个解
all_x = [] # 一组解
# 判断前k项是否冲突
def conflict(k):
global x
for i in range(k): # 遍历前x[0]~x[k-1]
if x[i] == x[k] or abs(x[i]-x[k]) == abs(i-k):
return True
return False
def queen(k):
global n, x, all_x
if k >= n: # 超出最底行
# 此处注意深浅拷贝
all_x.append(x[:])
# 如果使用X.append(x)的话,将会出现[[][]···[]]的情况。
for i in range(n):
x.append(i)
if not conflict(k): # 剪枝(如果没有冲突的话,则开始下一列)
queen(k+1) # 此处使用递归
x.pop() # 回溯,出栈
# 解的可视化
def show(x):
global n
for i in range(n):
print('O'*(x[i]) + 'X'+'O'*(n-x[i]-1))
# 测试
queen(0) # 从第0行开始
print("总共有%d种情况" % (len(all_x)))
print(all_x[0])
show(all_x[0])
突发奇想:
对于k皇后问题情况分布是如何的呢?
不妨测试一下:
for i in range(4, 11):
n = i
x = [] # 一个解
all_x = [] # 一组解
queen(0) # 从第0行开始
print("K = %d时,总共有%d种情况" % (i,len(all_x)))
# print(all_x[0])
结果如下:
K = 4时,总共有2种情况
K = 5时,总共有10种情况
K = 6时,总共有4种情况
K = 7时,总共有40种情况
K = 8时,总共有92种情况
K = 9时,总共有352种情况
K = 10时,总共有724种情况
貌似也没有看出什么规律。
【题材来源:Python算法详解,张玲玲,人民邮电出版社P42】