问题描述
给定一个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
初步思路:
生成全排列,使用itertools.permutations(list)内置函数或回溯都可,然后累加判断最后的和是否符合题意,结果40分超时:
import copy
n,sum=map(int,input().split())
result=[]
nums=[i for i in range(1,n+1)]
class Solution:
def __init__(self):
self.path=[]
self.result=[]
def solve(self,nums):
used=[False]*len(nums)
self.backtracking(nums,used)
return self.result
def backtracking(self,nums,used):
if len(self.path)==len(nums):
self.result.append(self.path[:])
return
for i in range(len(nums)):
if used[i]==True:
continue
self.path.append(nums[i])
used[i]=True
self.backtracking(nums,used)
self.path.pop()
used[i]=False
s=Solution()
result=s.solve(nums)
li=copy.deepcopy(result)
def sumd(li):
for i in range(len(li)-1):
for j in range(len(li)-1):
li[j]=li[j]+li[j+1]
return li[0]
N=[]
for i in range(len(li)):
if sumd(li[i])==sum:
N.append(result[i])
print(*min(N))
进阶思路:
百度得知,与杨辉三角有关,所求排列等于杨辉三角最后一行对应相乘求和等于目标sum既满足要求,故再定义一个产生杨辉三角最后一行的函数,60分超时:
n,sum=map(int,input().split())
def YH(n):
yh=[[0]*n for i in range(n)]
for i in range(n):
yh[i][i]=1
yh[i][0]=1
for i in range(n):
for j in range(n):
if i>1:
yh[i][j]=yh[i-1][j-1]+yh[i-1][j]
return yh[n-1]
result=[]
nums=[i for i in range(1,n+1)]
class Solution:
def __init__(self):
self.path=[]
self.result=[]
self.yh=YH(n)
def solve(self,nums,sum):
used=[False]*len(nums)
self.backtracking(nums,used)
return self.result
def backtracking(self,nums,used):
if len(self.path)==len(nums):
if self.is_Valid(self.path,self.yh,sum):
self.result.append(self.path[:])
return
for i in range(len(nums)):
if used[i]==True:
continue
self.path.append(nums[i])
used[i]=True
self.backtracking(nums,used)
self.path.pop()
used[i]=False
def is_Valid(self,li1,li2,target):
for i in range(len(li1)):
target-=li1[i]*li2[i]
if target==0:
return True
else:
return False
s=Solution()
result=s.solve(nums,sum)
print(*min(result))
最终版:
将求和的过程内置在回溯中,且由于原始数组是有序,故第一个符合的path就是字典序最小的数组,故舍弃result直接输出path,这里用flag来起到直接结束回溯而防止继续递归搜索的目的:满分
n,sum=map(int,input().split())
def YH(n):
yh=[[0]*n for i in range(n)]
for i in range(n):
yh[i][i]=1
yh[i][0]=1
for i in range(n):
for j in range(n):
if i>1:
yh[i][j]=yh[i-1][j-1]+yh[i-1][j]
return yh[n-1]
nums=[i for i in range(1,n+1)]
class Solution:
def __init__(self):
self.path=[]
self.result=[]
self.yh=YH(n)
self.flag=0
def solve(self,nums,sum):
used=[False]*(n+1)
self.backtracking(nums,used,sum)
return self.result
def backtracking(self,nums,used,sum):
if self.flag==1:return
if self.sumd(self.path,self.yh)>sum:
return
if len(self.path)==len(nums):
if self.sumd(self.path,self.yh)==sum:
print(*self.path[:])
self.flag=1
else:
return
for i in range(len(nums)):
if used[i]==True:
continue
self.path.append(nums[i])
used[i]=True
self.backtracking(nums,used,sum)
self.path.pop()
used[i]=False
def sumd(self,li1,li2):
sum_now=0
for i in range(len(li1)):
sum_now+=li1[i]*li2[i]
return sum_now
s=Solution()
s.solve(nums,sum)