魔法甜点之和:小包的新挑战题解 | 豆包MarsCode AI 刷题

67 阅读4分钟

问题分析

这个问题的要求是计算出一个角色(小R)在使用限定数量的魔法棒 (M) 的情况下,能否通过选择甜点的甜度值组合,达到目标甜度值 (S)。每种甜点有一个固定的甜度值,使用魔法棒可以将甜点的甜度值替换成其阶乘值,从而提高甜度。最终的目标是找出达到目标甜度值 S 的所有方案数量。

解决这个问题时,需要分别考虑以下几种情况:

  1. 添加当前甜点的甜度值。
  2. 使用魔法棒将甜点的甜度值替换为其阶乘值。
  3. 跳过当前甜点不使用。

每种选择都构成了不同的方案,符合题意的方案总数即为解。

解题思路

该方案利用了 动态规划记忆化搜索 的技巧来高效地计算可能的组合方案。下面是详细的解题思路:

  1. 状态表示

    • 定义递归函数 dfs(index, used_magic, current_sum),其中:

      • index 表示当前处理的甜点序号。
      • used_magic 表示已使用的魔法棒数量。
      • current_sum 表示当前累积的甜度值。
  2. 边界条件

    • 如果 current_sum 超过目标 Sused_magic 超过 M,则当前路径无效,返回 0。
    • 如果遍历了所有甜点(即 index == n)且 current_sum 恰好等于 S,则说明找到一个符合条件的方案,返回 1。
    • 如果遍历了所有甜点但 current_sum 不等于 S,则当前方案无效,返回 0。
  3. 记忆化

    • 使用字典 memo 缓存每个状态 (index, used_magic, current_sum) 的计算结果,以避免重复计算,提高效率。
  4. 递归处理

    • 对于每个甜点,可以选择以下三种操作:

      1. 直接加入甜度值:将当前甜度值加入 current_sum,不使用魔法棒。
      2. 使用魔法棒:将当前甜度值替换为其阶乘值后加入 current_sum(前提是还可以使用魔法棒)。
      3. 跳过当前甜点:不计入当前甜点的甜度值,直接处理下一个甜点。
    • 以上三种操作的结果累加就是当前状态的所有有效方案数。

  5. 初始化递归

    • 从第一个甜点开始,used_magic 初始化为 0,current_sum 初始化为 0。

代码


from math import factorial

def solution(n, m, s, like):
    # 状态记录 memo[(index, used_magic, current_sum)] = ways
    memo = {}
    
    def dfs(index, used_magic, current_sum):
        # 如果超出目标和或者魔法棒超出数量,返回 0
        if current_sum > s or used_magic > m:
            return 0
        # 如果到达最后一个甜点
        if index == n:
            return 1 if current_sum == s else 0
        # 检查缓存
        if (index, used_magic, current_sum) in memo:
            return memo[(index, used_magic, current_sum)]
        
        # 不使用魔法棒的情况
        res = dfs(index + 1, used_magic, current_sum + like[index])
        # 使用魔法棒的情况(如果还能用)
        if used_magic < m:
            res += dfs(index + 1, used_magic + 1, current_sum + factorial(like[index]))
        # 跳过当前甜点
        res += dfs(index + 1, used_magic, current_sum)
        
        # 缓存结果
        memo[(index, used_magic, current_sum)] = res
        return res
    
    # 从第 0 个甜点开始
    return dfs(0, 0, 0)

# 测试用例
print(solution(3, 2, 6, [1, 2, 3]) == 5)  # 示例 1
print(solution(3, 1, 1, [1, 1, 1]) == 6)  # 示例 2
print(solution(5, 3, 24, [1, 2, 3, 4, 5]) == 1)  # 示例 3
print(solution(4, 0, 10, [1, 3, 3, 3]) == 1)  # 示例 4
print(solution(6, 1, 35, [5, 5, 5, 5, 5, 5]) == 0)  # 示例 5

复杂度分析

from math import factorial

def solution(n, m, s, like): # 状态记录 memo[(index, used_magic, current_sum)] = ways memo = {}

def dfs(index, used_magic, current_sum):
    # 如果超出目标和或者魔法棒超出数量,返回 0
    if current_sum > s or used_magic > m:
        return 0
    # 如果到达最后一个甜点
    if index == n:
        return 1 if current_sum == s else 0
    # 检查缓存
    if (index, used_magic, current_sum) in memo:
        return memo[(index, used_magic, current_sum)]
    
    # 不使用魔法棒的情况
    res = dfs(index + 1, used_magic, current_sum + like[index])
    # 使用魔法棒的情况(如果还能用)
    if used_magic < m:
        res += dfs(index + 1, used_magic + 1, current_sum + factorial(like[index]))
    # 跳过当前甜点
    res += dfs(index + 1, used_magic, current_sum)
    
    # 缓存结果
    memo[(index, used_magic, current_sum)] = res
    return res

# 从第 0 个甜点开始
return dfs(0, 0, 0)

复杂度分析

  • 时间复杂度:该算法的时间复杂度在最坏情况下为指数级,取决于甜点数目 n、魔法棒数 m 及目标甜度值 S 的组合。不过,利用记忆化缓存可以避免重复计算,显著减少实际运行时间。
  • 空间复杂度:使用了 O(n * m * s) 的空间存储缓存,记录每个状态的计算结果