问题描述
小R不再追求甜点中最高的喜爱值,今天他想要的是甜点喜爱值之和正好匹配他的预期值 S。为了达到这个目标,他可以使用魔法棒来改变甜点的喜爱值,使其变为原来喜爱值的阶乘。每个甜点只能使用一次魔法棒,也可以完全不用。
下午茶小哥今天带来了 N 个甜点,每个甜点都有一个固定的喜爱值。小R有 M 个魔法棒,他可以选择任意甜点使用,但每个甜点只能使用一次魔法棒。他的目标是通过选择一些甜点,可能使用魔法棒,使得这些甜点的喜爱值之和恰好为 S。
请计算小R有多少种不同的方案满足他的要求。如果两种方案中,选择的甜点不同,或者使用魔法棒的甜点不同,则视为不同的方案。
数学重述
小 R 面对 N 个甜点,每个甜点都有固定的喜爱值( like 值 )。为了满足他独特的需求,他想要选取一些甜点使得它们的喜爱值之和正好等于 S。为了实现这一目标,小 R 有 M 根魔法棒,每根魔法棒可以将甜点的喜爱值变为该值的阶乘(例如,2 的阶乘为 )。
每个甜点可以:
- 原样选择。
- 使用魔法棒变成喜爱值的阶乘。
目标是计算满足条件的所有可能方案的总数。如果两种方案中选择的甜点或使用魔法棒的甜点不同,则视为不同方案。
问题分析
这是一个典型的动态规划问题,但相比传统背包问题增加了以下复杂性:
-
状态扩展:不仅可以选择甜点原始值,还可以选择其阶乘值。
-
双重限制:方案受两方面的限制 —— 总和 和魔法棒的使用次数 。
样例分析
输入:
输出:
样例分析: 有 5 种方案让喜爱值之和为 :
-
不使用魔法棒:[1, 2, 3]
-
使用一次魔法棒:阶乘变换 ,结果:[1, 6]
解题思路
使用动态规划( Dynamic Programming, DP )来解决,定义状态和转移方式如下:
- 状态定义:
- :表示使用了 根魔法棒,且选取甜点后总和为 的方案数。
- 状态转移:
- 如果当前甜点使用了魔法棒,其阶乘值加入总和,更新方案数。
- 如果当前甜点不使用魔法棒,其原始值加入总和,更新方案数。
算法步骤
- 预处理:计算 0 到最大值的阶乘,存储在数组
magic中,便于快速查询。 - 初始化状态:初始时 (没有选甜点,没有使用魔法棒,总和为 0 的方案数为 1)。
- 逐步处理每个甜点:
- 对每种可能的状态 ,尝试两种选择:
- 不使用魔法棒:总和增加原始喜爱值。
- 使用魔法棒:总和增加阶乘值,并增加魔法棒使用数。
- 对每种可能的状态 ,尝试两种选择:
- 统计结果:所有符合条件的 累加,得到最终方案数。
代码分解
1. 函数定义与输入解析
定义了解决问题的主函数,参数包括字符串的长度 ( n )、可用操作次数 ( k )、以及由 '0' 和 '1' 组成的字符串 ( s )。通过将字符串转换为列表形式,便于进行后续的字符交换操作。
def solution(n: int, k: int, s: str) -> str:
s = list(s) # 将字符串转换为列表以便操作
2. 遍历字符串中的每个字符
逐个检查字符串的每个位置。如果当前字符为 '0',无需操作,直接跳过。若剩余的操作次数 ( k ) 为 0,说明已经无法再进行交换,直接退出循环。
for i in range(n):
if k <= 0:
break # 如果没有剩余的操作次数,退出
if s[i] == '0':
continue # 如果当前字符是 '0',则无需移动
3. 寻找并替换右侧的最近 '0'
从当前字符右侧寻找最近的 '0',并计算将其移动到当前位置所需的交换次数。如果所需交换次数 ( \text{needed_swaps} ) 不超过剩余的操作次数 ( k ),执行交换操作并减少 ( k )。每找到一个可用的 '0' 就退出内层循环,避免重复处理。
# 需要将字符 '0' 移到当前位置 i 之前
for j in range(i + 1, n):
if s[j] == '0':
# 计算需要的交换次数
needed_swaps = j - i
if needed_swaps <= k:
# 执行交换
for swap in range(j, i, -1):
s[swap], s[swap - 1] = s[swap - 1], s[swap]
k -= needed_swaps # 减少可用的交换次数
break # 找到一个 '0' 之后就可以退出内层循环
4. 返回最终结果
将修改后的字符列表重新转换为字符串,并返回结果。
return ''.join(s)
5. 测试代码
通过多组测试用例验证代码的正确性,确保输出符合期望结果。
if __name__ == '__main__':
print(solution(5, 2, "01010") == '00101') # True
print(solution(7, 3, "1101001") == '0110101') # True
print(solution(4, 1, "1001") == '0101') # True