66.魔法甜点之和:小包的新挑战|Marscode AI刷题

1.题目

问题描述

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

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

请计算小R有多少种不同的方案满足他的要求。如果两种方案中,选择的甜点不同,或者使用魔法棒的甜点不同,则视为不同的方案。


测试样例

样例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

2.样例解析

样例1:

输入:n = 3, m = 2, s = 6, like = [1, 2, 3]

输出:5

方案一:选中第三个甜点,使用魔法。次数1。

方案二:选中三个甜点,都不使用魔法。次数0。

方案三:选中三个甜点,且对第一个使用魔法。次数1.

方案四:选中三个甜点,且对第二个使用魔法。次数1.

方案五:选中三个甜点,且对第一个和第二个使用魔法。次数2次。总和是1! +2! +3=1+2+3=6。此时魔法次数是2,满足m=2的条件。

3.思路

  • 定义状态和转换

    • 我们可以使用动态规划(DP)来解决这个问题。
    • dpmap[(stick_count, like_sum)]:表示当前已选择了 stick_count 个魔法棒,且当前的喜爱值总和为 like_sum 时的选择方式数。
    • 初始时,dpmap[(0, 0)] = 1,即还没有选择任何甜点,魔法棒数目为 0,喜爱值和为 0,只有 1 种选择方式(空集)。
  • 状态转移

    • 对于每个甜点,有两种选择:
      1. 不使用魔法棒:直接将当前甜点的喜爱值加入到 like_sum 中。
      2. 使用魔法棒:使用魔法棒后的喜爱值为阶乘值,将其加入到 like_sum 中,同时增加魔法棒的数量。

    通过遍历所有状态,并依次更新 dpmap,考虑每个甜点的两种选择方式。

  • 更新 dpmap

    • 每次选择甜点时,先将当前状态 dpmap 进行复制,避免在遍历时修改原始数据。
    • 对于每个状态 (stick_count, like_sum),尝试两种选择:
      • 加入当前甜点的普通喜爱值,如果不超过 s,更新状态。
      • 加入当前甜点的阶乘喜爱值,并且使用魔法棒数不超过 m,更新状态。
  • 计算结果

    • 最终结果是所有使用不超过 m 个魔法棒,且喜爱值总和为 s 的所有选择方式数。通过遍历 dpmap,累计所有符合条件的选择方式。

4.代码

from collections import defaultdict
# 初始化一个 magic 数组,用来存储每个整数的阶乘,最大支持到 99!
magic = [1] * 100
def solution(n, m, s, like):
    # Please write your code here
    # 计算每个整数的阶乘并存储到 magic 数组中
    for i in range(1, 100):
        magic[i] = magic[i - 1] * i
    # dpmap 是一个默认字典,用于记录每种状态的方案数
    # 键是一个元组 (stick_count, like_sum),分别表示使用了多少个魔法棒和目前的喜爱值之和
    # 值是该状态下的方案数
    dpmap = defaultdict(int) # 默认值类型为 int
    dpmap[(0, 0)] = 1 # 没有选择任何甜点,没有使用魔法棒,喜爱值和为 0 的状态有 1 种方式(即空集)
    # 遍历每一个甜点
    for i in range(n):
        # copy 是为了防止当前状态被污染,保证每次遍历都不会受到修改的影响
        current_dpmap = dpmap.copy()

        # 遍历所有当前已存储的状态
        for (stick_count, like_sum), count in current_dpmap.items():
            # 情况 1: 当前甜点不使用魔法棒
            if like_sum + like[i - 1] <= s:
                # 如果加入当前甜点不超过目标值 S,则更新状态
                dpmap[(stick_count, like_sum + like[i - 1])] += count
            # 情况 2: 当前甜点使用魔法棒
            if stick_count + 1 <= m and like_sum + magic[like[i - 1]] <= s:
                # 如果使用魔法棒后的喜爱值和不超过 S 且魔法棒的数量没有超过 m,则更新状态
                dpmap[(stick_count + 1, like_sum + magic[like[i - 1]])] += count
    # 最终的结果是所有使用不超过 m 个魔法棒,且喜爱值和为 S 的方案数之和
    result = 0
    for i in range(m + 1):
        result += dpmap[(i, s)]
    return result

if __name__ == "__main__":
    #  You can add more test cases here
    print(solution(3, 2, 6, [1,2,3]) == 5 )
    print(solution(3, 1, 1, [1,1,1]) == 6 )

5.参考资料

【题解】魔法甜点之和:小包的新挑战 | 豆包MarsCode AI刷题本文通过DP结合空间离散化(使用哈希表存储状态)的 - 掘金

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值