2回溯法
回溯法是一个求解“全部解”问题的方法。
回溯法是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法。而满足回溯条件的某个状态的点称为“回溯点”。
2.1八皇后问题
第一 分别尝试所有的位置。
第二 可以分别尝试所有的位置,前提条件是这个位置不能和已有的皇后冲突,只能放第二排。
第三 可以分别尝试所有的位置,前提条件是这个位置不能和已有的皇后冲突,只能放第三排。
结束条件:
第八个皇后放完以后。
# 八皇后问题
存储空间
board = [
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0]
]
# 方案数量
total = 0
def can_place(x, y):
# 判断(x,y)坐标能否放皇后
# 1.判断x行是否有皇后
for i in range(0, y):
if board[x][i] == 1:
return False
# 2.判断y列是否有皇后
for i in range(0, x):
if board[i][y] == 1:
return False
# 3.判断斜线"/"方向是否有皇后
# 第i行对应位置的列标为x+y-i
for i in range(0, x):
if x + y - i <= 7 and board[i][x + y - i] == 1:
return False
# 4. 判断"\"方向是否有皇后
# index:0->(x-1),i:(x-1)->0
for index, i in enumerate(range(x - 1, -1, -1)):
s_y = y - (index + 1)
if s_y >= 0:
if board[i][s_y] == 1:
return False
return True
def print_board():
# 打印方案
for i in range(8):
for j in range(8):
if board[i][j] == 0:
print("□ ", end=" ")
else:
print("■ ", end=" ")
print()
def put_queen(step):
# 八皇后问题迭代
if step == 8:
print_board()
global total
total += 1
print("-----------------------------------------")
else:
for i in range(8):
# 判断该位置是否能放置当前皇后
if can_place(step, i):
# 1.设置现场
board[step][i] = 1
# 2.开始递归
put_queen(step + 1)
# 3.恢复现场
board[step][i] = 0
if __name__ == "__main__":
put_queen(0)
print("总共有{}种方法".format(total))
运行结果:
2.2全排列问题
# 全排列问题
# 1.老大 选择一个,剩余的交给老二
# 2.老二 选择一个,剩余的交给老三
data_list = [1, 2, 3, 4, 5]
arranges = [] # 排列方案
total = 0 # 方案数
def search(depth, datas):
if depth == len(data_list) + 1:
# arranges.append(datas[0])
print(arranges)
# arranges.pop()
global total
total += 1
else:
for data in datas:
# 1.设置现场
arranges.append(data)
# 2.递归
next_datas = datas[:]
next_datas.remove(data)
search(depth + 1, next_datas)
# 3.恢复现场
arranges.pop()
if __name__ == "__main__":
search(1, data_list)
print("共有{}种排列方式".format(total))
2.3数字拆分算法
# 1.老大拿到这个数之后,先尝试所有可能的取值,上一个分给自己的值
datas = [] # 存放组合方案
total = 0 # 方案总数
def search(rest):
if rest <= 0:
print(datas)
global total
total += 1
else:
for i in range(1, rest + 1):
# 1.设置现场
datas.append(i)
# 2.递归
search(rest - i)
# 3.恢复现场
datas.pop()
if __name__ == "__main__":
num = 7 # 要被插的数
search(num)
print("方案数有{}种".format(total))
运行结果:
2.4总结
八皇后问题和全排列问题的迭代次数是固定的,相对来说会比较简单;数字拆分算法迭代次数是动态变化的。
回溯法的核心流程分为三部分:
- 设置现场。将当前数据放到结果集。
- 迭代。
- 恢复现场。将刚刚放的到结果集中的数据取出来,方便下一次迭代。