试题 算法训练 数字游戏【Python】

题目:
题目
第一个想法,求出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)
  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是丝豆呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值