卡牌正反面数字和的整除方案数求解
问题描述
小M有 张卡牌,每张卡牌的正反面分别写着不同的数字,正面是 ,背面是 。小M希望通过选择每张卡牌的一面,使得所有向上的数字之和可以被 整除。你需要告诉小M,一共有多少种不同的方案可以满足这个条件。由于可能的方案数量过大,结果需要对 取模。
示例1:
输入:n = 3,a = [1, 2, 3],b = [2, 3, 2]
输出:3
示例2:
输入:n = 4,a = [3, 1, 2, 4],b = [1, 2, 3, 1]
输出:6
示例3:
输入:n = 5,a = [1, 2, 3, 4, 5],b = [1, 2, 3, 4, 5]
输出:32
问题分析
思路概述
本题要求计算满足特定条件的方案总数,即选择每张卡牌的正面或背面,使得所有向上的数字之和能被 整除。由于每张卡牌都有两个选择(正面或背面),并且我们需要考虑总和的模 值,这提示我们可以使用动态规划来解决这个问题。
状态定义
设 dp[s] 表示当前已处理的卡牌,其向上数字之和模 为 的方案数。由于模 的结果只有 三种可能,所以 dp 数组的大小为 。
转移方程
对于每张卡牌,我们有两个选择:
- 选择正面数字 。
- 选择背面数字 。
我们需要更新 dp,使其包含当前卡牌的选择结果。对于每个可能的前一状态 s,我们可以通过选择当前卡牌的正面或背面,计算新的模 值,并累加方案数。
当 时:
for s in range(3):
for num in [a_i, b_i]:
new_mod = (s + num) % 3
temp_dp[new_mod] = (temp_dp[new_mod] + dp[s]) % MOD
当 时:
即使正反面的数字相同,但由于卡牌的两面不同,我们仍然有两种不同的选择(正面朝上或反面朝上)。因此,方案数需要乘以 。
for s in range(3):
new_mod = (s + a_i) % 3
temp_dp[new_mod] = (temp_dp[new_mod] + dp[s] * 2) % MOD
初始条件
最初,没有选任何卡牌时,和为 ,因此 dp[0] = 1。
代码详解
def solution(n: int, a: list, b: list) -> int:
MOD = 10**9 + 7 # 模数
dp = [0] * 3 # dp[s] 表示和模 3 为 s 的方案数
dp[0] = 1 # 初始条件,和为 0 的方案数为 1
for i in range(n):
temp_dp = [0] * 3 # 用于更新 dp 的临时数组
if a[i] == b[i]:
# 当正反面数字相同,但选择不同,方案数要乘以 2
for s in range(3):
new_mod = (s + a[i]) % 3
temp_dp[new_mod] = (temp_dp[new_mod] + dp[s] * 2) % MOD
else:
# 正反面数字不同,分别考虑选择 a[i] 和 b[i]
for s in range(3):
for num in [a[i], b[i]]:
new_mod = (s + num) % 3
temp_dp[new_mod] = (temp_dp[new_mod] + dp[s]) % MOD
dp = temp_dp # 更新 dp
return dp[0] % MOD # 返回和能被 3 整除的方案数
代码解释
-
初始化:
dp[0] = 1,表示初始状态下,和为 的方案数为 。 -
遍历每张卡牌:
-
创建一个临时数组
temp_dp,用于存储更新后的方案数。 -
当 时:
- 对于每个可能的前一状态
s,计算新的模 值new_mod,方案数累加dp[s] * 2(因为有两种选择,但数字相同)。
- 对于每个可能的前一状态
-
当 时:
- 对于每个可能的前一状态
s,分别考虑选择a_i和b_i,计算新的模 值,方案数累加dp[s]。
- 对于每个可能的前一状态
-
-
更新
dp: 将temp_dp的值赋给dp,进入下一次迭代。 -
结果返回: 最终
dp[0]即为满足条件的方案数。
测试样例验证
示例1:
输入:n = 3,a = [1, 2, 3],b = [2, 3, 2]
通过上述算法,计算得出方案数为 3,与示例输出一致。
示例2:
输入:n = 4,a = [3, 1, 2, 4],b = [1, 2, 3, 1]
计算得出方案数为 6,与示例输出一致。
示例3:
输入:n = 5,a = [1, 2, 3, 4, 5],b = [1, 2, 3, 4, 5]
计算得出方案数为 32,与示例输出一致。
复杂度分析
- 时间复杂度: ,其中 为卡牌数量。因为我们需要遍历每张卡牌,对于每张卡牌的操作都是常数级别。
- 空间复杂度: 。
dp数组的大小固定为 ,与 无关。
总结
本题通过将问题转化为模 的动态规划,巧妙地解决了组合数求解的问题。在处理相同数字的卡牌时,注意到虽然数字相同,但选择的面不同,仍然算作不同的方案,需要倍乘。这一细节是解题的关键所在。
通过本题,我们学习了如何将求组合数的问题转化为状态转移问题,以及在动态规划中处理模运算的技巧。这对于解决类似的组合计数问题具有重要的参考价值。