迷宫问题
- 如下图:小球必须避开红色墙体移动,直到找到箭头所指的出口,请用代码实现
思路
利用递归和回溯,递归的终止条件是假设已经移动到了出口位置,上图中箭头位置即是maze_map[6][5];在此基础上,约定从maze_map[1][1] 出发,按照:下 右 上 左 的行进策略移动小球,然后假设 第一个点是没有走过的,即它为0,然后假设 当前位置能走同,即标为2 那么 在2的位置 它可以走四个方向,先试探第一个约定的位置:向下走,此时用递归,不断去找路,如果能走通都标为2,直到走不通则原路返回,返回到第一个方向的递归处,再来试探 右,以此类推,直到能走到maze_map[6][5]位置!
Python实现
class Maze(object):
def __init__(self):
self.r = 0 # 统计有多少行
self.c = 0 # 统计有多少列
# 先用 二维数组模拟一个迷宫,墙体设为1,上下全部置为1
def print_maze(self, array_maze): # 打印迷宫地图
for row_item in array_maze:
for col_item in row_item:
print(col_item, end=" ")
print()
def create_maze(self):
row = 8
col = 7
maze_map = [[0 for i in range(col)] for j in range(row)]
self.r = len(maze_map) # 有多少行 8
self.c = len(maze_map[0]) # 有多少列 7
# 上下全部置为1 即墙体
for i in range(self.c): # 这应该是7 取0-6的值,因为变得是列
maze_map[0][i] = 1
maze_map[self.r - 1][i] = 1
# 左右全部置为1 即墙体
for j in range(1, self.r):
maze_map[j][0] = 1
maze_map[j][self.c - 1] = 1
# 设置挡板
maze_map[3][1] = 1
maze_map[3][2] = 1
# maze_map[1][2] = 1
# maze_map[2][2] = 1
return maze_map
# 利用递归和回溯为小球寻找路径
# 约定,row,col,表示从地图的哪个位置出发,约定从(1,1)出发,到(6,5)结束,则找到通路
# 当:maze_map [row][col] 为0 表示该点没有走过,为1则为墙,2表示通路可以走,3表示该路已经走过,但是走不通
# 策略:行进的方向:下 右 上 左 ,如果该点走不通则回溯
def find_way(self, maze_map, row, col):
"""
:param maze_map: 传入一张地图
:param row: 位置
:param col: 位置
:return: 如果找到通路就返回True,否则返回False
"""
if maze_map[self.r - 2][self.c - 2] == 2: # 最后一个位置如果2为通路,即递归的出口
return True
else:
if maze_map[row][col] == 0: # 如果当前这个点还没有走过,那按照策略:下 右 上 左 走
maze_map[row][col] = 2 # 假定该点是可以走通的
if self.find_way(maze_map, row + 1, col): # 向下走,行加1
return True
elif self.find_way(maze_map, row, col + 1): # 走不通,尝试向右走
return True
elif self.find_way(maze_map, row - 1, col): # 走不通,尝试向上走
return True
elif self.find_way(maze_map, row, col - 1): # 走不通,尝试向左走
return True
else: # 尝试后,改点走不通,是死路,所以置为3
maze_map[row][col] = 3
return False
else: # 如果maze_map[row][col]!=0 ,则可能是 1,2,3;
# 因为1是墙不考虑,2是通路不需要重复,剩下3是死路直接返回False即可
return False
if __name__ == '__main__':
obj = Maze()
get_map = obj.create_maze()
obj.print_maze(get_map) # 输出第一次制作的地图
print("<===========>")
obj.find_way(get_map, 1, 1)
obj.print_maze(get_map) # 输出策略:下 右 上 左 策略的地图
print("<===========>")
obj.find_way(get_map, 1, 1)
体现该过程中 产生回溯:放开create_maze方法中的“ maze_map[1][2] = 1 ,maze_map[2][2] = 1”
即多设置两面墙,那么还是从同样的位置出发,发现走不通,则都设为3
如何找出最短路径
- 最短路径是 根据最开始的策略来判断的,所以我们只需要改变行进策略再求解,就能得出了
- 前提和递归方式都不需要变,解决改变发生递归的行进方向,如下面实现:行进策略为:上 右 下 左
class Maze(object):
def __init__(self):
self.r = 0 # 统计有多少行
self.c = 0 # 统计有多少列
# 先用 二维数组模拟一个迷宫,墙体设为1,上下全部置为1
def print_maze(self, array_maze): # 打印迷宫地图
for row_item in array_maze:
for col_item in row_item:
print(col_item, end=" ")
print()
def create_maze(self):
row = 8
col = 7
maze_map = [[0 for i in range(col)] for j in range(row)]
self.r = len(maze_map) # 有多少行 8
self.c = len(maze_map[0]) # 有多少列 7
# 上下全部置为1 即墙体
for i in range(self.c): # 这应该是7 取0-6的值,因为变得是列
maze_map[0][i] = 1
maze_map[self.r - 1][i] = 1
# 左右全部置为1 即墙体
for j in range(1, self.r):
maze_map[j][0] = 1
maze_map[j][self.c - 1] = 1
# 设置挡板
maze_map[3][1] = 1
maze_map[3][2] = 1
# maze_map[1][2] = 1
# maze_map[2][2] = 1
return maze_map
# 利用递归和回溯为小球寻找路径
# 约定,row,col,表示从地图的哪个位置出发,约定从(1,1)出发,到(6,5)结束,则找到通路
# 当:maze_map [row][col] 为0 表示该点没有走过,为1则为墙,2表示通路可以走,3表示该路已经走过,但是走不通
# 策略:行进的方向:下 右 上 左 ,如果该点走不通则回溯
def find_way(self, maze_map, row, col):
"""
:param maze_map: 传入一张地图
:param row: 位置
:param col: 位置
:return: 如果找到通路就返回True,否则返回False
"""
if maze_map[self.r - 2][self.c - 2] == 2:
return True
else:
if maze_map[row][col] == 0: # 如果当前这个点还没有走过,那按照策略:下 右 上 左 走
maze_map[row][col] = 2 # 假定该点是可以走通的
if self.find_way(maze_map, row + 1, col): # 向下走,行加1
return True
elif self.find_way(maze_map, row, col + 1): # 走不通,尝试向右走
return True
elif self.find_way(maze_map, row - 1, col): # 走不通,尝试向上走
return True
elif self.find_way(maze_map, row, col - 1): # 走不通,尝试向左走
return True
else: # 否则改点走不通,是死路,所以置为3
maze_map[row][col] = 3
return False
else: # 如果maze_map[row][col]!=0 ,则可能是 1,2,3;
# 因为1是墙可以不考虑,2是通路不需要重复,剩下3是死路直接返回False即可
return False
# 新策略:行进的方向:下 右 上 左 ,如果该点走不通则回溯
def find_new_way(self, maze_map, row, col):
"""
:param maze_map: 传入一张地图
:param row: 位置
:param col: 位置
:return: 如果找到通路就返回True,否则返回False
"""
if maze_map[self.r - 2][self.c - 2] == 2:
return True
else:
if maze_map[row][col] == 0: # 如果当前这个点还没有走过,那按照策略:下 右 上 左 走
maze_map[row][col] = 2 # 假定该点是可以走通的
# 特别注意,是新方法的递归 ↓↓↓self.find_new_way
if self.find_new_way(maze_map, row - 1, col): # 向上走,行减1
return True
elif self.find_new_way(maze_map, row, col + 1): # 走不通,尝试向右走
return True
elif self.find_new_way(maze_map, row + 1, col): # 走不通,尝试向下走
return True
elif self.find_new_way(maze_map, row, col - 1): # 走不通,尝试向左走
return True
else: # 否则改点走不通,是死路,所以置为3
maze_map[row][col] = 3
return False
else: # 如果maze_map[row][col]!=0 ,则可能是 1,2,3;
# 因为1是墙可以不考虑,2是通路不需要重复,剩下3是死路直接返回False即可
return False
if __name__ == '__main__':
obj = Maze()
get_map = obj.create_maze()
obj.print_maze(get_map) # 输出第一次制作的地图
# print("<===========>")
# obj.find_way(get_map, 1, 1)
# obj.print_maze(get_map) # 输出策略:下 右 上 左 策略的地图
print("<===========>")
obj.find_new_way(get_map, 1, 1)
obj.print_maze(get_map) # 输出策略:上 右 下 左 策略的地图
八皇后问题
思路
Python实现
汉诺塔问题
思路
- 概述:
- 回溯思路
用一维数组的解释:对array 的 索引+1 表示第几行,也即是第几个皇后,array[i]=val,这个val值表示第i+1个皇后,放在i+1列,所以arr[8]={0,4,7,5,2,6,1,3} 表示 第一个皇后,放在第一行第一列的位置,第二个皇后放在第二行第五列的位置,第三个皇后放在第三行第八列的位置…
Python实现
- 先补充一些小知识点
(1)如何创建有一定长度和初始值的列表
array = [None]*8
print(len(array)) # 8
(2)Python中求绝对值的方法
# 三种方法求绝对值
import math
# 条件判断
def abs_value1():
a = float(input('1.请输入一个数字:'))
if a >= 0:
a = a
else:
a = -a
print('绝对值为:%f' % a)
# 用abs
def abs_value2():
a = float(input('2.请输入一个数字:'))
a = abs(a)
print('绝对值为:%f' % a)
# 内置函数
def abs_value3():
a = float(input('3.请输入一个数字:'))
a = math.fabs(a)
print('绝对值为:%f' % a)
abs_value1()
abs_value2()
abs_value3()
完整实现 八皇后问题
class EightQueen(object):
def __init__(self, max_queen=8):
self.max_queen = max_queen # 皇后的数量
self.array = [0] * self.max_queen # 创建一个带长度的一维数组
self.count = 0 # 用来统计
self.judge_count = 0 # 用来统计判断冲突的次数
def check(self, n): # 放置第n个皇后的方法;检查它后方的是否满足
# check 是每一次递归时,进入check中都有for i in range(self.max_queen),因此会有回溯
if n == self.max_queen: # n=8 其实8个皇后已经放好了,注意下标是从0开始的
self.print_queen()
return
# 依次放入皇后并判断是否冲突
for i in range(self.max_queen): # 总共放8个皇后,总共8行
# 先把当前的皇后n放到该行的第i列,即初始的皇后位置为:第一行第一列
self.array[n] = i
# 判断当放置第n个皇后到i列时(它前面的),是否冲突
if self.judge(n): # 不冲突
# 接着放第n+1个皇后,即开始递归
self.check(n + 1)
# 如果冲突,就回去执行self.array[n] = i,i会自动加等于1,等于自动换一列
def judge(self, n): # 查看当放置第n个皇后,去检测该皇后是否和前方已经摆放的皇后冲突
'''
:param n: 表示第n个皇后
:return: boolean
'''
self.judge_count += 1 # 判断调用了几次
for i in range(n):
# self.array[i] == self.array[n] 判断第n个皇后是否和前面n-1皇后在同一列
# abs(n - i) == abs(self.array[n] - self.array[i]) 判断第n个皇后是否和第i个皇后在同一斜线
# i是不断在变化的,只有当i变化到刚好是n前一个时,才可能出现在同一斜线情况
if self.array[i] == self.array[n] or abs(n - i) == abs(self.array[n] - self.array[i]):
return False
return True # 注意是所有的都检查完
def print_queen(self): # 输出皇后摆放的位置
self.count += 1 # 看它调了多少次,就有多少种结果
for index in range(len(self.array)):
print(self.array[index], end=" ")
print("")
if __name__ == '__main__':
obj = EightQueen()
obj.check(0)
print("总共有:%d 种解法" % obj.count)
print("总共判断了:%d 次冲突" % obj.judge_count)
- 下面是结果的一部分,总共有92种解法
…
简化版
扫描二维码关注公众号,回复: 9192428 查看本文章
def queen(A, cur=0):
if cur == len(A):
print(A)
return 0
for col in range(len(A)):
A[cur], flag = col, True
for row in range(cur):
if A[row] == col or abs(col - A[row]) == cur - row:
flag = False
break
if flag:
queen(A, cur + 1)
queen([None] * 8)