一、回溯是什么?
我觉得可能就是一种好听的学术的暴力遍历,当然加上剪枝以后就会高大上一些,不过暴力遍历也能加简化条件就是了
二、实例
1.回溯法模板
回溯法理解了以后就可以套模板了,这里是我自己定义的模板:
def backtrack(A,re):
if A 满足条件:
re.append(A)
for i in 可选择的下一状态:
if 可以剪枝:
剪枝
A.append(i)
backtrack(A,re)
A.remove(i) #remove了没有关系,因为满足条件的项已经加入re了
2.实例:求子集
这里用了一种回溯发,也可以用二进制代表法,下图是非二进制代表法的回溯图:
def subset(A,S,re,te):
if A not in re:
re.append(A[:])
for i in range(te,len(S)):
t,te=S[i],i
A.append(t)
subset(A,S,re,te+1)
A.remove(t)
re=[]
subset([],[1,2,3],re,0)
print(re)
3.实例:做数独
为了偷懒呢,我这里只做了一个3*3的数独格子,且需要满足的条件也只有横竖都有123即可,比较增加条件只需要在模板的 if 中添加即可,没有什么技术难题:
import copy
def isSafe(A):
mode=[x for x in range(1,len(A)+1)]
for i in range(1,len(A)+1):
t=mode[:]
for j in range(1,len(A)+1):
if A[i-1][j-1] in t:
t.remove(A[i-1][j-1])
else:
return False
for i in range(1,len(A)+1):
t=mode[:]
for j in range(1,len(A)+1):
if A[j-1][i-1] in t:
t.remove(A[j-1][i-1])
else:
return False
return True
def find_emp(A): #寻找还没填数的下标
r=[]
for i in range(len(A)):
for j in range(len(A)):
if A[i][j]==0:
r.append([i,j])
return r
def sudoku(A,re):
emp_pos=find_emp(A)
if isSafe(A) and A not in re and len(emp_pos)==0:
re.append(copy.deepcopy(A))
elif len(emp_pos)>0:
for x in range(1,4):
emp_pos=find_emp(A)
i,j=emp_pos[0]
A[i][j]=x
sudoku(A,re)
A[i][j]=0
re=[]
A=[[0,3,0],[0,0,1],[2,0,0]]
sudoku(A,re)
print(re)
总结
回溯法想明白了真的不难,没想明白前真的很难,其实回头想起来最关键的点就是为什么可以remove?因为remove前已经把答案加入re啦!如果回溯法不递归那么一下就和暴力没什么区别,所以emmm好吧