题目解析:## 魔法甜点之和:小包的新挑战
小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
问题描述分析
从代码的结构来看,问题的核心是给定一个长度为 n 的数组 like,数组中的每个元素代表某种物品的“喜欢数量”。我们需要从中选择某些物品,且要满足一些条件。具体的条件和目标如下:
- 你可以选择最多
m个物品。 - 你选择的物品喜欢的数量的总和不能超过
s。 - 每个物品不仅可以选择其“喜欢的数量”值,还可以选择其“喜欢数量的阶乘值”。
代码分析
1. 魔法数组 magic 和阶乘预处理
pythonCopy Code
magic = [1] * 100
def pre():
for i in range(1, 100):
magic[i] = magic[i - 1] * i
这里定义了一个长度为 100 的数组 magic,用于存储从 0! 到 99! 的阶乘值。pre() 函数负责对这个数组进行初始化,预计算好阶乘值,避免在后续的计算中重复计算阶乘。
2. 动态规划的状态转移
pythonCopy Code
def solution(n, m, s, like):
pre()
f = defaultdict(int) # 使用defaultdict来初始化map
f[(0, 0)] = 1 # 初始状态
f 是一个字典,用于记录状态,键是一个元组 (a, b),表示当前选中了 a 个物品,并且这些物品的“喜欢数量的总和”是 b。值是该状态下的组合数量。初始状态 (0, 0) 代表没有选择任何物品时,组合数量为 1。
3. 动态规划的主循环
pythonCopy Code
for i in range(1, n + 1):
g = f.copy() # 备份当前的状态
for (a, b), v in g.items():
if b + like[i - 1] <= s: # 加入"喜欢"的数量
f[(a, b + like[i - 1])] += v
if a + 1 <= m and b + magic[like[i - 1]] <= s: # 加入"喜欢的阶乘"
f[(a + 1, b + magic[like[i - 1]])] += v
对于每个物品 i,我们都要尝试两种选择:
- 加入物品的“喜欢数量” :当前的总和
b加上物品i的like[i-1](表示物品的喜欢数量),如果加上后不超过s,就将该状态的组合数量v加到新的状态(a, b + like[i-1])。 - 加入物品的“喜欢数量的阶乘” :如果当前已选物品数
a + 1不超过m且当前总和b + magic[like[i-1]](物品的阶乘值)不超过s,那么就将该状态的组合数量v加到新的状态(a + 1, b + magic[like[i-1]])。
注意,g 是一个备份的字典,避免在遍历过程中修改原始字典 f 导致数据丢失。
4. 计算结果
pythonCopy Code
sum_ = 0
for i in range(m + 1):
sum_ += f[(i, s)] # 累加满足条件的结果
最后,遍历所有可能选择物品的数量 a(从 0 到 m),累加那些“喜欢数量的总和等于 s”的状态下的组合数,得到最终的结果。
5. 主函数和测试用例
pythonCopy Code
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 )
测试用例通过比较函数 solution 的返回值与预期值来验证程序的正确性。
总结
这段代码的核心思想是通过动态规划(DP)来解决一个多重选择的组合问题,其中每个物品有两种选择:选择其喜欢的数量或选择其喜欢数量的阶乘值。通过字典存储每个状态(物品数和喜欢数量和),并通过状态转移逐步更新所有可能的组合方式,最终统计出满足条件的组合数。
主要技巧:
- 动态规划:利用
f[(a, b)]来记录状态,避免重复计算。 - 备份字典:使用
g = f.copy()来避免在遍历过程中修改原始状态。 - 阶乘预处理:通过
magic数组预先计算阶乘值,避免重复计算。
心得:
使用MarsCode AI编写代码让我体验到了编程的便利与高效。AI能够快速生成代码示例,帮助我理解不同编程概念。通过交互式的反馈,我能迅速调整思路,解决问题。同时,MarsCode AI提供的建议让我了解到更多最佳实践,提升了我的编码水平。这种工具不仅节省了时间,还激发了我的创造力,尤其是在处理复杂问题时,AI的支持显得尤为重要。总的来说,MarsCode AI是编程学习和实践中的得力助手。