题目:
第一个想法,求出1~n的全排列,按字典序排列,然后从头依次对排列进行操作,得出结果在和sum对比,找出第一个符合要求的序列。
结果这样做会超时,只有四十分
all_s = [] # 存储所有序列
def perm(h, r): # 求所有的序列
global all_s, lst
if h >= r:
all_s.append(lst.copy())
return
for i in range(h, r):
lst[i], lst[h] = lst[h], lst[i]
perm(h+1, r)
lst[i], lst[h] = lst[h], lst[i]
n, m = map(int, input().split())
lst = [x for x in range(1, n+1)]
perm(0, n)
all_s.sort() # 按字典序对所有排列进行排序
# print(all_s)
for i in all_s:
l = len(i)
temp = i.copy()
while l: # 按题目要求求和
for j in range(0, l-1):
i[j] = i[j] + i[j+1]
l -= 1
if i[0] == m: # 如果符合要求就输出序列,并退出循环
for p in temp: print(p, end=" ")
break
第二个想法,通过观察序列中每个数字被加的次数,可以发现每个数字被加的次数和n-1的所有组合数有关,就拿样例来说
3 1 2 4
4 3 6(1 ,2 ,2 ,1)
7 9(1 , 3 , 3 , 1)= (C30 , C31 , C32 , C33)
16
所以我们可以先求出n-1的所有组合数,然后对每个组合数枚举对应位置的数字
def COMB(x): # 动态规划求组合数,也可以用python自带的函数求,math.comb
lst = [[1 for _ in range(x+1)] for _ in range(x+1)]
for i in range(2, x+1):
for j in range(1, i):
lst[i][j] = lst[i-1][j] + lst[i-1][j-1]
return lst[x]
def solution(n, m, lst, vis, p, temp, re):
if p == n:
if temp == m:
print(str(re).strip('[]').replace(',', ''))
vis[0] = 1 # 标记,代表已经找到答案,第一个输出的找到的排列就是答案
return 0
if not vis[0]: # 当标记为1时,代表当前已经输出了最优答案,不需要在继续找了
for i in range(1, n+1):
if vis[i] == 0:
re[p] = i # 记录当前位置数字
vis[i] = 1 # 标记这个数字用过了
if temp+re[p]*lst[p] <= m: # 约束条件,当和大于m时,是无效的解,没有这个约束条件只有90分
solution(n, m, lst, vis, p+1, temp+re[p]*lst[p], re)
vis[i] = 0 # 将数字恢复到没用过的状态,递归回来的时候让后面的位置可以用这个数字
n, m = map(int, input().split())
re = [0 for _ in range(n)] # 记录排列
lst = COMB(n-1) # 存储n-1的所有组合数
# print(lst)
vis = [0 for _ in range(n+1)] # 标记数组,防止排列中数字重复
solution(n, m, lst, vis, 0, 0, re)