- 如何选择下一个没有选择过的数 :i?? 使用一个数组:used 存储。
- 下一个选择的数字 i 和 last数, 之间需要满足某些条件。
- 马字步
- 同行或者同列
- 对角线的相邻
- last 和 i 中间的元素被使用过
- 使用回溯法
解2, 通过设置坐标。
对1~9设置坐标pos字典:
{1: [0, 0], 2: [0, 1], 3: [0, 2], 4: [1, 0], 5: [1, 1], 6: [1, 2], 7: [2, 0], 8: [2, 1], 9: [2, 2]}
再设置
l
i
m
i
t
limit
limit
列表中任意值
l
i
m
i
t
[
i
]
[
j
]
limit[i][j]
limit[i][j]的含义为从数字i到j需要经过
l
i
m
i
t
[
i
]
[
j
]
limit[i][j]
limit[i][j]
如果
l
i
m
i
t
[
i
]
[
j
]
=
0
limit[i][j]=0
limit[i][j]=0,说明两个数值可以直接到达
class Solution:
def numberOfPatterns(self, m: int, n: int) -> int:
'''
dfs算法,带入的参数分别为:
index: 上一次滑动经过的数字
used: 数字的使用情况,由于题目于要求点不能重复,所以如果数字被使用过,则略过该情况
limit: 保存数字滑动限制条件的列表
num: 已经滑动的数字个数
res: 计算结果
m,n: 题目中提供的上下界
dfs算法的一般流程为:
1. 设置返回/终止条件,本题中终止条件为数字个数超过上界
2. 考虑返回值,题目中的返回值为res
3. 进行循环遍历,值得注意的是,每次遍历结束后,都应将条件初始化,防止影响下一次计算
'''
def dfs(index,used,limit,num,res,m,n):
'''
1. 设置终止条件:当num超过上界时,返回res值
'''
if num>n:
return res
'''
2. 考虑当计算过程中的返回值,num在[m,n]之间时,res+1
'''
if m<=num<=n:
res += 1
'''
3. 开始循环遍历,每次从[1,9]中选择一个数值作为下一步要经过的数字i,
当这个数值未被使用过,且上一步经过的数字index与i之间需要经过的数字
已经被使用过时,才进行循环。
'''
for i in range(1,10):
if used[i] == False and used[limit[index][i]]==True:
# i被使用了,所以标记为使用过的数字
# 经过的数字数num也要相应+1
used[i] = True
num += 1
#开始循环
res = dfs(i,used,limit,num,res,m,n)
# 初始化条件,即i未被使用过
# 同时经过的数字数为原来的num
used[i] = False
num -= 1
return res
'''
为了建立保存数字滑动限制条件的列表limit,首先要考虑每个数字在手机键盘中的位置
'''
pos={}
for i in range(1,10):
pos[i] = [(i-1)//3,i-1-(i-1)//3*3]
print(pos)
'''
计算limit列表
为了使数字与行列数一致,这里建立了一个10*10的列表,初始值为0
列表中任意值limit[i][j]的含义为从数字i到j需要经过limit[i][j]
如果limit[i][j]=0,说明两个数值可以直接到达
也就是说默认used[0]=True
'''
limit = [[0]*10 for i in range(10)]
for i in range(1,10):
for j in range(i+1,10):
x_i,y_i = pos[i] #pos[1] => (0,0)
x_j,y_j = pos[j]
#判断条件:九宫格中,两个数字的行数和列数至少有一个相差值为2,同时两数之和为偶数
if (abs(x_i-x_j)==2 or abs(y_i-y_j)==2) and (i+j)%2==0:
limit[i][j] = (i+j)//2 # 判断中间的数为几,然后used[几]看是否是True
limit[j][i] = (i+j)//2
#所有值的初始化
index = 0
used = [True]+[False]*9
num = 0
res = 0
res = dfs(index,used,limit,num,res,m,n)
return res