66 魔法甜点之和:小包的新挑战

47 阅读3分钟

题解:甜点喜爱值之和问题

题目分析

小R的问题可以建模为一个状态搜索问题,通过动态规划或深度优先搜索(DFS)的方法找到所有符合条件的组合。

问题的核心在于:

  1. 对于每个甜点,有三种选择:
    • 不选该甜点。
    • 选该甜点的原始喜爱值。
    • 选该甜点的阶乘值(消耗一个魔法棒)。
  2. 目标是找到所有满足甜点喜爱值之和恰好为 (S) 的方案。

问题的约束和变量:

  • (N) 表示甜点的数量。
  • (M) 表示魔法棒的最大数量。
  • (S) 表示目标喜爱值。
  • (like[i]) 表示第 (i) 个甜点的初始喜爱值。

求解思路

本题需要考虑所有可能的选择组合,可以采用深度优先搜索(DFS)+ 记忆化搜索动态规划 的方式来求解。


求解思路详解

1. 状态定义

使用 DFS,定义以下状态:

  • 当前甜点索引 (index)。
  • 已使用的魔法棒数量 (used_magic)。
  • 当前甜点喜爱值之和 (current_sum)。
2. 状态转移

对于每个甜点,存在三种选择:

  1. 不选择当前甜点:状态转移为 (dfs(index+1, used_magic, current_sum))。
  2. 选择原始值:状态转移为 (dfs(index+1, used_magic, current_sum + like[index]))。
  3. 选择阶乘值(需要魔法棒):前提是剩余魔法棒数量足够,状态转移为 (dfs(index+1, used_magic+1, current_sum + factorial(like[index])))。
3. 终止条件
  • 当 (current_sum > S) 或 (used_magic > M) 时,返回 0。
  • 当 (index == N) 时,判断是否 (current_sum == S),如果相等返回 1,否则返回 0。
4. 记忆化搜索

为了避免重复计算,将中间状态结果存储在 memo 中,键为 ((index, used_magic, current_sum))。


代码实现

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
        # 如果到达所有甜点并且和刚好为 s,返回 1 种方案
        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)
        # 使用当前甜点的原始值
        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]))

        # 记录当前状态
        memo[(index, used_magic, current_sum)] = res
        return res

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

# 测试用例
if __name__ == "__main__":
    print(solution(3, 2, 6, [1, 2, 3]))  # 输出:5
    print(solution(3, 1, 1, [1, 1, 1]))  # 输出:6
    print(solution(5, 3, 24, [1, 2, 3, 4, 5]))  # 输出:1
    print(solution(4, 0, 10, [1, 3, 3, 3]))  # 输出:1
    print(solution(6, 1, 35, [5, 5, 5, 5, 5, 5]))  # 输出:0

代码解析

  1. DFS 函数

    • 遍历每种可能的选择(不使用、使用原始值、使用阶乘值)。
    • 判断是否满足约束条件(超出 (S) 或使用魔法棒数超过 (M) 时剪枝)。
    • 递归搜索剩余的甜点。
  2. 记忆化

    • 避免重复计算相同的状态。
    • 减少搜索次数,提高效率。
  3. 复杂度分析

    • 状态空间大小为 (O(n \times m \times S)),其中:
      • (n):甜点数量。
      • (m):魔法棒数量。
      • (S):目标和。
    • 由于记忆化的优化,时间复杂度近似为状态空间大小。

测试结果

样例:

  • 样例1:(n = 3, m = 2, s = 6, like = [1, 2, 3])
    输出:5。

  • 样例2:(n = 3, m = 1, s = 1, like = [1, 1, 1])
    输出:6。

  • 样例3:(n = 5, m = 3, s = 24, like = [1, 2, 3, 4, 5])
    输出:1。

  • 样例4:(n = 4, m = 0, s = 10, like = [1, 3, 3, 3])
    输出:1。

  • 样例5:(n = 6, m = 1, s = 35, like = [5, 5, 5, 5, 5, 5])
    输出:0。


总结

该问题使用 DFS+记忆化搜索 有效解决:

  • 考虑了所有可能的组合方案。
  • 避免重复计算,显著提高了效率。
  • 代码结构清晰,便于理解和扩展。