一、卡牌翻面求和问题
问题理解
小M有 n 张卡牌,每张卡牌的正反面分别写着不同的数字,正面是 a[i],背面是 b[i]。小M希望通过选择每张卡牌的一面,使得所有向上的数字之和可以被 3 整除。你需要计算有多少种不同的方案可以满足这个条件。由于可能的方案数量过大,结果需要对 10^9 + 7 取模。
示例:
- 输入:
n = 3,a = [1, 2, 3],b = [2, 3, 2] - 输出:
3 - 解释:有3种方案使得和可以被3整除。
- 分别是:
a[0]a[1]a[2]、a[0]b[1]b[2]、b[0]a[1]b[2]
数据结构选择
- 动态规划(DP)数组:用来记录到每张卡牌时,选择某些卡牌使得和模
3余j的方案数。定义dp[i][j]表示前i张卡牌中,选择某些卡牌使得和模3余j的方案数。 - 整数变量:用于计数和索引操作。
算法步骤
-
初始化:
- 定义
MOD = 10^9 + 7,用于结果取模。 - 初始化 DP 数组
dp,大小为(n + 1) x 3,所有值设为0。 - 初始状态
dp[0][0] = 1,表示不选任何卡牌时,和为0的方案数为1。
- 定义
-
动态规划填表:
-
遍历每张卡牌
i:-
获取当前卡牌的正面和背面的数字
num1 = a[i-1]和num2 = b[i-1]。 -
对于每个余数
j(0到2):- 更新选择正面的情况:
dp[i][(j + num1) % 3] = (dp[i][(j + num1) % 3] + dp[i - 1][j]) % MOD - 更新选择背面的情况:
dp[i][(j + num2) % 3] = (dp[i][(j + num2) % 3] + dp[i - 1][j]) % MOD
- 更新选择正面的情况:
-
-
-
返回结果:
- 最终答案为
dp[n][0],表示前n张卡牌中,选择某些卡牌使得和模3余0的方案数。
- 最终答案为
代码实现
def solution(n: int, a: list, b: list) -> int:
MOD = 10**9 + 7
# 初始化dp数组,dp[i][j]表示前i张卡牌中,选择某些卡牌使得和模3余j的方案数
dp = [[0] * 3 for _ in range(n + 1)]
dp[0][0] = 1 # 初始状态,不选任何卡牌时,和为0的方案数为1
for i in range(1, n + 1):
num1 = a[i - 1]
num2 = b[i - 1]
# 更新dp数组
for j in range(3):
# 选择正面的情况
dp[i][(j + num1) % 3] = (dp[i][(j + num1) % 3] + dp[i - 1][j]) % MOD
# 选择背面的情况
dp[i][(j + num2) % 3] = (dp[i][(j + num2) % 3] + dp[i - 1][j]) % MOD
# 最终答案为dp[n][0],表示前n张卡牌中,选择某些卡牌使得和模3余0的方案数
return dp[n][0]
二、魔法甜点之和:小包的新挑战
问题理解
小R有 N 个甜点,每个甜点都有一个固定的喜爱值。小R有 M 个魔法棒,可以选择任意甜点使用,但每个甜点只能使用一次魔法棒。使用魔法棒可以将甜点的喜爱值变为原来的阶乘。小R的目标是通过选择一些甜点,可能使用魔法棒,使得这些甜点的喜爱值之和恰好为 S。需要计算有多少种不同的方案满足这个要求。如果两种方案中,选择的甜点不同,或者使用魔法棒的甜点不同,则视为不同的方案。
数据结构选择
- 动态规划(DP)数组:用来记录到每个甜点时,选择某些甜点使得和为
k的方案数。定义dp[i][j][k]表示前i个甜点中,使用j个魔法棒,使得和为k的方案数。 - 整数变量:用于计数和索引操作。
算法步骤
-
初始化:
- 定义
max_s = S + 1,用于方便处理边界。 - 初始化 DP 数组
dp,大小为(N + 1) x (M + 1) x max_s,所有值设为0。 - 初始状态
dp[0][0][0] = 1,表示不选任何甜点时,和为0的方案数为1。
- 定义
-
动态规划填表:
-
遍历每个甜点 i:
-
获取当前甜点的喜爱值
current_like = like[i - 1]和其阶乘current_factorial = factorial(current_like)。 -
对于每个魔法棒数量
j(0到M):-
对于每个可能的和
k(0到max_s - 1):- 不使用当前甜点:
dp[i][j][k] += dp[i - 1][j][k] - 使用当前甜点,不使用魔法棒:如果
k >= current_like,则dp[i][j][k] += dp[i - 1][j][k - current_like] - 使用当前甜点,使用魔法棒:如果
j > 0且k >= current_factorial,则dp[i][j][k] += dp[i - 1][j - 1][k - current_factorial]
- 不使用当前甜点:
-
-
-
-
计算结果:
- 计算所有使用
M个魔法棒的方案数:total_ways = sum(dp[N][j][S] for j in range(M + 1))
- 计算所有使用
-
返回结果:
- 返回
total_ways,表示满足条件的方案数。
- 返回
代码实现
def factorial(n):
if n == 0 or n == 1:
return 1
result = 1
for i in range(2, n + 1):
result *= i
return result
def solution(n, m, s, like):
max_s = s + 1 # 为了方便处理边界
dp = [[[0] * max_s for _ in range(m + 1)] for _ in range(n + 1)]
dp[0][0][0] = 1 # 初始状态,不选任何甜点时,和为0的方案数为1
for i in range(1, n + 1):
current_like = like[i - 1]
current_factorial = factorial(current_like)
for j in range(m + 1):
for k in range(max_s):
# 不使用当前甜点
dp[i][j][k] += dp[i - 1][j][k]
# 使用当前甜点,不使用魔法棒
if k >= current_like:
dp[i][j][k] += dp[i - 1][j][k - current_like]
# 使用当前甜点,使用魔法棒
if j > 0 and k >= current_factorial:
dp[i][j][k] += dp[i - 1][j - 1][k - current_factorial]
# 计算所有使用 m 个魔法棒的方案
total_ways = sum(dp[n][j][s] for j in range(m + 1))
return total_ways