魔法甜点-小宝的挑战| 豆包MarsCode AI刷题

57 阅读6分钟

问题描述

小R不再追求甜点中最高的喜爱值,今天他想要的是甜点喜爱值之和正好匹配他的预期值 S。为了达到这个目标,他可以使用魔法棒来改变甜点的喜爱值,使其变为原来喜爱值的阶乘。每个甜点只能使用一次魔法棒,也可以完全不用。

下午茶小哥今天带来了 N 个甜点,每个甜点都有一个固定的喜爱值。小R有 M 个魔法棒,他可以选择任意甜点使用,但每个甜点只能使用一次魔法棒。他的目标是通过选择一些甜点,可能使用魔法棒,使得这些甜点的喜爱值之和恰好为 S。

请计算小R有多少种不同的方案满足他的要求。如果两种方案中,选择的甜点不同,或者使用魔法棒的甜点不同,则视为不同的方案。 代码解读和实现`def factorial(n): if n == 0 or n == 1: return 1 result = 1 for i in range(2, n + 1): result *= i return result

def solution(n, m, s, like): # 计算阶乘值 factorials = [factorial(x) for x in like]

# 初始化 DP 数组
dp = [[[0 for _ in range(s + 1)] for _ in range(m + 1)] for _ in range(n + 1)]
dp[0][0][0] = 1  # 基础状态

for i in range(1, n + 1):
    original = like[i - 1]
    fact = factorials[i - 1]
    
    for j in range(m + 1):
        for k in range(s + 1):
            # 不使用魔法棒
            dp[i][j][k] += dp[i - 1][j][k]  # 不选这个甜点
            
            if k >= original:
                dp[i][j][k] += dp[i - 1][j][k - original]  # 选这个甜点,不用魔法
                
            if j > 0 and k >= fact:
                dp[i][j][k] += dp[i - 1][j - 1][k - fact]  # 选这个甜点,使用魔法
            
# 汇总所有可能使用的魔法棒数量
return sum(dp[n][j][s] for j in range(m + 1))
  1. 阶乘计算

    • 使用 factorial 函数计算每个甜点的阶乘值,并存储在 factorials 列表中。
  2. 动态规划数组初始化

    • dp[i][j][k] 表示在前 i 个甜点中,使用 j 个魔法棒,使得喜爱值之和为 k 的方案数。
    • 初始化 dp[0][0][0] = 1,表示不选任何甜点且不使用魔法棒时,喜爱值之和为 0 的方案数为 1。
  3. 状态转移

    • 对于每个甜点 i,可以选择不使用魔法棒、使用魔法棒或不选这个甜点。
    • 更新 dp[i][j][k] 时,考虑所有可能的情况,并累加相应的方案数。
  4. 结果汇总

    • 最终结果是所有可能使用的魔法棒数量 j 对应的 dp[n][j][s] 的和。 -` for i in range(1, n + 1): original = like[i - 1] fact = factorials[i - 1]

      for j in range(m + 1): for k in range(s + 1): # 不使用魔法棒 dp[i][j][k] += dp[i - 1][j][k] # 不选这个甜点

          if k >= original:
              dp[i][j][k] += dp[i - 1][j][k - original]  # 选这个甜点,不用魔法
              
          if j > 0 and k >= fact:
              dp[i][j][k] += dp[i - 1][j - 1][k - fact]  # 选这个甜点,使用魔法`
      

    针对代码解读: 这是一个三重嵌套的循环,外层循环控制遍历每个甜点(从第1个到第n个,索引为i)。对于每个甜点,先获取它对应的两个属性值:original(从like列表中获取,可能表示选择该甜点本身消耗的资源量等)和fact(从factorials列表中获取,可能与使用魔法棒操作该甜点相关的属性)。

然后内部的两层嵌套循环(分别遍历魔法棒数量j和资源量等k的所有可能取值范围)来进行状态转移的计算:

  • dp[i][j][k] += dp[i - 1][j][k]:这行代码表示在当前考虑第i个甜点时,如果不选择这个甜点(也就是不使用魔法棒去处理它),那么当前状态dp[i][j][k]的方案数量应该等于上一个甜点(即i - 1个甜点)在相同魔法棒数量j和相同资源量k情况下的方案数量,相当于继承了之前的状态。
  • if k >= original:这个条件判断语句下的dp[i][j][k] += dp[i - 1][j][k - original]:表示当当前的资源量k足够选择这个甜点(即大于等于该甜点本身消耗的资源量original)时,在不使用魔法棒的情况下选择这个甜点后的方案数量,应该是在上一个甜点(i - 1个甜点)在相同魔法棒数量j,但资源量剩余为k - original情况下的方案数量基础上累加,也就是增加了选择当前甜点这种新的方案情况。
  • if j > 0 and k >= fact:这个条件判断语句下的dp[i][j][k] += dp[i - 1][j - 1][k - fact]:表示当还有魔法棒可以使用(j > 0)并且当前的资源量k足够满足使用魔法棒操作这个甜点(大于等于对应的阶乘值fact)时,选择这个甜点并且使用魔法棒后的方案数量,应该是在上一个甜点(i - 1个甜点)在少一个魔法棒(j - 1)且资源量剩余为k - fact情况下的方案数量基础上累加,相当于增加了使用魔法棒选择当前甜点这种新的方案情况。 基于marscode ai的辅助提示,我对改代码能进行进一步的更改和加强
  1. 阶乘计算优化

    • 使用 memo 字典来存储已经计算过的阶乘值,避免重复计算。
  2. 动态规划数组优化

    • 使用滚动数组来优化空间复杂度,减少内存使用。通过从后向前遍历,避免覆盖之前的值。 -def solution(n, m, s, like): # 计算阶乘值,使用优化后的factorial函数 factorials = [factorial(x) for x in like] # 初始化优化后的DP滚动数组,只保留当前和上一层相关状态所需空间 dp = [[0 for _ in range(s + 1)] for _ in range(m + 1)] prev_dp = [[0 for _ in range(s + 1)] for _ in range(m + 1)] prev_dp[0][0] = 1 # 基础状态 for i in range(1, n + 1): original = like[i - 1] fact = factorials[i - 1] # 从后向前遍历,避免覆盖之前还未使用的值 for j in range(m, -1, -1): for k in range(s, -1, -1): dp[j][k] = prev_dp[j][k] # 不使用魔法棒,先继承上一层状态 if k >= original: dp[j][k] += prev_dp[j][k - original] # 选这个甜点,不用魔法 if j > 0 and k >= fact: dp[j][k] += prev_dp[j - 1][k - fact] # 选这个甜点,使用魔法 # 更新prev_dp,为下一轮迭代做准备 prev_dp = [row.copy() for row in dp] # 汇总所有可能使用的魔法棒数量 return sum(prev_dp[j][s] for j in range(m + 1))

    一、算法理解与运用能力

  • 动态规划深入学习

    • 通过对代码中动态规划部分的剖析,深入理解动态规划的核心概念,包括如何定义状态、确定状态转移方程以及设定边界条件等。可以尝试改变问题的一些条件或者规模,重新推导状态转移过程,强化对这一算法解决实际问题的能力。
    • 基于此代码,进一步拓展去解决更多不同类型但同样适用动态规划的题目,例如背包问题的变种、最长公共子序列问题的拓展等,通过不断实践加深对动态规划在不同场景下应用的理解,熟练掌握动态规划算法的各种优化技巧。