回溯算法(全排列问题):
数字游戏
问题描述
给定一个1~N的排列a[i],每次将相邻两个数相加,得到新序列,再对新序列重复这样的操作,显然每次得到的序列都比上一次的序列长度少1,最终只剩一个数字。
例如:
3 1 2 4
4 3 6
7 9
16
现在如果知道N和最后得到的数字sum,请求出最初序列a[i],为1~N的一个排列。若有多种答案,则输出字典序最小的那一个。数据保证有解。
输入格式
第1行为两个正整数n,sum
输出格式
一个1~N的一个排列
样例输入
4 16
样例输出
3 1 2 4
数据规模和约定
0<n<=10
1、 杨辉三角的第N行与该题的最初序列a[i]之积为最后得到的数字sum。如(1 3 3 1)*(3 1 2 4)=16
杨辉三角
1
1 1
1 2 1
1 3 3 1
2、 由1可知,我们只要找出N的全排列,并与杨辉三角的第N行相乘,如果和为sum,则将其筛选出来,得到a[i]。由于题目是找字典序最小的一个,因此我们从小到大搜索,当满足要求,则返回结果,不再继续搜索。此时我们需要解决两个问题,首先是返回杨辉三角的最后一行,其次是全排列问题,全排列可以用回溯算法。
返回杨辉三角的最后一行
#返回杨辉三角的最后一行
N=int(input())
l=[[0 for i in range(N)]for j in range(N)]
def yh(l):
for i in range(N):
for j in range(N):
if i==0 and j==0:
l[i][j]=1
elif j==0:
l[i][j] = 1
elif j==i:
l[i][j]=1
else:
l[i][j]=l[i-1][j-1]+l[i-1][j]
return l[N-1]
找1-N个数的全排列
用一个数组V标记已使用过的数字,单层搜索时遍历v数组,当遇到v[i]为0 时,将nums[i]加入路径,并进行递归搜索,当走到叶子节点时,撤销处理结果,开始回溯。
# 找1-N个数的全排列
l=list(map(int,input().split()))
v=[0 for i in range(len(l))]
path=[]#存放单个排列
res=[]#存放全部排列结果
def huisu(step,l):
#找递归终止条件,当到叶子节点时返回,开始回溯
if step==len(l):
res.append(path.copy())
return
#单层搜索
for i in range(len(l)):
if v[i]==1:
continue
v[i]=1
path.append(l[i])
huisu(step+1,l)
path.pop()#撤销处理结果
v[i]=0
huisu(0,l)
print(res)
测试结果:
1 2 3
[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
针对该题,我们要得到的是满足一定要求的排列,因此要设一个参数insum,路径里面每加入一个数就更新一次(进入一层更新一次),直到进入最后一层,即到达叶子节点,判断当前的和insum是否满足题意:
N,sum=map(int,input().split())
l=[[0 for i in range(N)]for j in range(N)]
def yh(l):
for i in range(N):
for j in range(N):
if i==0 and j==0:
l[i][j]=1
elif j==0:
l[i][j] = 1
elif j==i:
l[i][j]=1
else:
l[i][j]=l[i-1][j-1]+l[i-1][j]
return l[N-1]
# 找1-N个数的全排列
v=[0 for i in range(N+1)]
path=[]#存放单个排列
res=[]#存放全部排列结果
y=yh(l)#杨辉三角的最后一行
flag=0
def huisu(step,insum):
global flag
if flag==1:
return
#找递归终止条件,当到叶子节点时返回,开始回溯
if step==N:
if insum==sum:
# res.append(path.copy())
print(' '.join(map(str,path)))
flag=1
return
#单层搜索
for i in range(1,N+1):#i从1开始
if v[i]==1:
continue
v[i]=1
path.append(i)
tsum=insum+i*y[step]#计算路径里面加入一个数时当前的和。在每一层进行更新
huisu(step+1,tsum)
path.pop()#撤销处理结果
v[i]=0
huisu(0,0)
# print(res)
测试结果:
4 16
3 1 2 4