题目解析
这个问题要求我们从每张卡牌上选择一个数字(可以是正面或背面),并且通过这些选择得到的数字和 ( S ) 能被 3 整除。我们需要求出所有符合条件的方案数。
问题本质
对于每张卡牌 ( i ),卡牌的正面数字是 ( a_i ),背面数字是 ( b_i ),我们可以选择 ( a_i ) 或 ( b_i )。我们要找出所有选择的和 ( S ) 满足 ( S \mod 3 = 0 )。
解题思路
1. 余数的性质
由于我们关注的是和 ( S ) 对 3 取余的结果,因此我们不需要关心具体的和,而只需要关心每一步的余数。对于每张卡牌,选择了正面或者背面后,数字和的余数会发生变化。
- 设定一个动态规划数组
dp[r],表示当前和 ( S ) 对 3 取余后的余数是 ( r ) 的方案数,( r ) 可以是 0, 1, 或 2。
2. 动态规划
我们可以通过动态规划来处理每张卡牌对余数的影响。具体步骤如下:
-
状态定义:
dp[0]、dp[1]和dp[2]分别表示当前选择的数字和的余数分别是 0、1、2 的方案数。 -
初始状态:初始化时,数字和为 0 的方案数为 1,其他余数的方案数为 0。即
dp[0] = 1,dp[1] = dp[2] = 0。 -
状态转移: 对于每张卡牌 ( i ),它提供了两种选择:
- 选择正面 ( a_i ):数字和的余数会从当前余数 ( r ) 转变为 ( (r + a_i) \mod 3 )。
- 选择背面 ( b_i ):数字和的余数会从当前余数 ( r ) 转变为 ( (r + b_i) \mod 3 )。
因此,我们需要用当前的余数来更新新的余数方案数。
-
目标:我们最终关心的是所有卡牌选择完之后,数字和模 3 为 0 的方案数,即
dp[0]。
3. 动态规划的实现步骤
对于每一张卡牌,我们需要更新 dp 数组。由于每张卡牌有两种选择,所以我们可以通过“拷贝”当前的 dp 数组来计算新的状态,并最终更新 dp。
代码实现
MOD = 10**9 + 7 # 由于题目可能要求输出的结果可能很大,使用取模
def count_ways_to_make_sum_divisible_by_3(n, cards):
# dp[i]表示当前和模3等于i的方案数
dp = [0] * 3
dp[0] = 1 # 初始时,和为0的方案数是1
for i in range(n):
a, b = cards[i]
# 当前卡牌a和b对dp的影响
new_dp = dp[:] # 复制当前的dp数组,准备更新
for r in range(3):
new_dp[(r + a) % 3] = (new_dp[(r + a) % 3] + dp[r]) % MOD
new_dp[(r + b) % 3] = (new_dp[(r + b) % 3] + dp[r]) % MOD
dp = new_dp # 更新dp为新的dp
return dp[0] # 返回最终和模3为0的方案数
# 示例
n = 3
cards = [(1, 2), (2, 3), (3, 2)]
print(count_ways_to_make_sum_divisible_by_3(n, cards)) # 输出:6
代码解释
-
初始化:初始化
dp = [1, 0, 0],表示开始时和为 0 的方案数为 1,其他余数的方案数为 0。 -
循环遍历每张卡牌:
- 每张卡牌有两个选择:正面 ( a_i ) 和背面 ( b_i )。
- 通过拷贝当前的
dp数组,计算出这两种选择对余数的影响。
-
更新 dp:对于每一个当前的余数 ( r ),如果选择了卡牌 ( i ) 的正面或背面,新的余数分别为 ( (r + a_i) \mod 3 ) 和 ( (r + b_i) \mod 3 ),因此需要更新新的
dp数组。 -
最终返回:最终我们关心的是
dp[0],即所有卡牌选择后和模 3 为 0 的方案数。
示例解析
对于输入:
n = 3
cards = [(1, 2), (2, 3), (3, 2)]
-
第一张卡牌有两种选择,( a_1 = 1 ) 或 ( b_1 = 2 )。
-
初始时
dp = [1, 0, 0],选择卡牌后:- 如果选择 ( a_1 = 1 ),则从余数 0 更新到余数 1,
dp[1] = 1。 - 如果选择 ( b_1 = 2 ),则从余数 0 更新到余数 2,
dp[2] = 1。
- 如果选择 ( a_1 = 1 ),则从余数 0 更新到余数 1,
-
所以,选择第一张卡牌后,
dp = [0, 1, 1]。
-
-
第二张卡牌有两种选择,( a_2 = 2 ) 或 ( b_2 = 3 )。
-
当前
dp = [0, 1, 1],选择卡牌后:-
选择 ( a_2 = 2 ) 后,余数会变化如下:
- 余数 0 → 2,余数 1 → 0,余数 2 → 1。
-
选择 ( b_2 = 3 ) 后,余数会变化如下:
- 余数 0 → 0,余数 1 → 1,余数 2 → 2。
-
-
所以,选择第二张卡牌后,
dp = [1, 2, 2]。
-
-
第三张卡牌有两种选择,( a_3 = 3 ) 或 ( b_3 = 2 )。
-
当前
dp = [1, 2, 2],选择卡牌后:-
选择 ( a_3 = 3 ) 后,余数会变化如下:
- 余数 0 → 0,余数 1 → 1,余数 2 → 2。
-
选择 ( b_3 = 2 ) 后,余数会变化如下:
- 余数 0 → 2,余数 1 → 0,余数 2 → 1。
-
-
所以,选择第三张卡牌后,
dp = [6, 4, 4]。
-
最终输出 dp[0] = 6,表示共有 6 种方案使得数字和模 3 等于 0。