问题分析
这个问题的要求是计算出一个角色(小R)在使用限定数量的魔法棒 (M) 的情况下,能否通过选择甜点的甜度值组合,达到目标甜度值 (S)。每种甜点有一个固定的甜度值,使用魔法棒可以将甜点的甜度值替换成其阶乘值,从而提高甜度。最终的目标是找出达到目标甜度值 S 的所有方案数量。
解决这个问题时,需要分别考虑以下几种情况:
- 添加当前甜点的甜度值。
- 使用魔法棒将甜点的甜度值替换为其阶乘值。
- 跳过当前甜点不使用。
每种选择都构成了不同的方案,符合题意的方案总数即为解。
解题思路
该方案利用了 动态规划 和 记忆化搜索 的技巧来高效地计算可能的组合方案。下面是详细的解题思路:
-
状态表示:
-
定义递归函数
dfs(index, used_magic, current_sum),其中:index表示当前处理的甜点序号。used_magic表示已使用的魔法棒数量。current_sum表示当前累积的甜度值。
-
-
边界条件:
- 如果
current_sum超过目标S或used_magic超过M,则当前路径无效,返回 0。 - 如果遍历了所有甜点(即
index == n)且current_sum恰好等于S,则说明找到一个符合条件的方案,返回 1。 - 如果遍历了所有甜点但
current_sum不等于S,则当前方案无效,返回 0。
- 如果
-
记忆化:
- 使用字典
memo缓存每个状态(index, used_magic, current_sum)的计算结果,以避免重复计算,提高效率。
- 使用字典
-
递归处理:
-
对于每个甜点,可以选择以下三种操作:
- 直接加入甜度值:将当前甜度值加入
current_sum,不使用魔法棒。 - 使用魔法棒:将当前甜度值替换为其阶乘值后加入
current_sum(前提是还可以使用魔法棒)。 - 跳过当前甜点:不计入当前甜点的甜度值,直接处理下一个甜点。
- 直接加入甜度值:将当前甜度值加入
-
以上三种操作的结果累加就是当前状态的所有有效方案数。
-
-
初始化递归:
- 从第一个甜点开始,
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)的空间存储缓存,记录每个状态的计算结果