数据结构与算法(Python)– 回溯法(Backtracking algorithm)
1、回溯法
回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。采用的是深度优先搜索。
2、迷宫
题目: 一个 N × M的二维数组 maze 表示一个迷宫,其中的1表示墙壁,0表示可以走,只能横着走或竖着走,不能斜着走,找出从左上角到右下角的最短路线。入口点为[0,0],既第一空格是可以走的。
回溯法:
# 走迷宫
def move(i, j):
global path
# 当前点变为1,表示已经走过,避免循环绕圈走不出去
maze[i][j] = 1
temp_path.append((i, j))
# 如果已经走到了出口,即右下角,判断是不是最短路径
if i == N - 1 and j == M - 1:
if len(path) == 0 or len(temp_path) < len(path):
path = []
path.extend(temp_path)
# 向下走
if 0 <= i + 1 < N and not maze[i + 1][j]:
move(i + 1, j)
# 向右走
if 0 <= j + 1 < M and not maze[i][j + 1]:
move(i, j + 1)
# 向上走
if 0 <= i - 1 < N and not maze[i - 1][j]:
move(i - 1, j)
# 向左走
if 0 <= j - 1 < M and not maze[i][j - 1]:
move(i, j - 1)
# 如果上下左右都走不通或者已经走到出口了,退回上一步,恢复现场
temp_path.pop()
maze[i][j] = 0
while True:
try:
# 输入迷宫大小
N, M = map(int, input().split())
maze = []
path = []
temp_path = []
# 输入迷宫地图
for _ in range(N):
maze.append(list(map(int, input().split())))
# 走迷宫
move(0, 0)
# 打印路径
for p in path:
print('(' + str(p[0]) + ',' + str(p[1]) + ')')
except:
break
3、数独
题目: 根据9X9网格上的已知数字,推算出所有剩余空格的数字,并且满足每一行、每一列、每一个3X3网格内的数字均含1-9,并且不重复。
回溯法:
# 检查该位置填该数字是否满足要求
def check(m,n,e):
# 检查行
if e in s[m]:
return False
# 检查列
if e in s_T[n]:
return False
# 检查3X3网格
x=m//3*3
y=n//3*3
for i in range(x,x+3):
for j in range(y,y+3):
if s[i][j]==e:
return False
return True
# 填数字
def move(k):
if k==81:
return 0
# 从左到右,从上到下遍历每一格
row,col=k//9,k%9
# 如果是该格为空
if s[row][col]=='0':
# 从1~9依次尝试
for e in num:
# 如果该数满足要求,该格填上该数
if check(row,col,e):
s[row][col]=e
s_T[col][row]=e
# 移到下一格。如果下一格不管填什么都不满足要求,恢复现场
if move(k+1):
s[row][col]='0'
s_T[col][row]='0'
else:
return 0
# 如果1~9都不满足,返回1
return 1
else:
if move(k+1):
return 1
while True:
try:
# 输入
s=[input().split() for i in range(9)]
s_T=[[s[i][j] for i in range(9)] for j in range(9)]
num=['1','2','3','4','5','6','7','8','9']
# 填数字
move(0)
# 打印
for i in range(9):
print(' '.join(s[i]))
except:
break
GOOD LUCK!