小M有 𝑛n 张卡牌,每张卡牌的正反面分别写着不同的数字,正面是 𝑎𝑖ai,背面是 𝑏𝑖bi。小M希望通过选择每张卡牌的一面,使得所有向上的数字之和可以被3整除。你需要告诉小M,一共有多少种不同的方案可以满足这个条件。由于可能的方案数量过大,结果需要对 109+7109+7 取模。
例如:如果有3张卡牌,正反面数字分别为 (1,2),(2,3) 和 (3,2),你需要找到所有满足这3张卡牌正面或背面朝上的数字之和可以被3整除的组合数。
问题理解
你需要找到所有可能的卡牌组合,使得这些卡牌正面或背面朝上的数字之和可以被3整除。每张卡牌有两个选择:正面或背面。
数据结构选择
由于我们需要计算所有可能的组合,并且需要快速判断一个组合是否满足条件,动态规划(DP)是一个合适的选择。我们可以使用一个数组来记录当前所有可能的和模3的结果。
算法步骤
-
初始化:创建一个长度为3的数组
dp,用于记录当前所有可能的和模3的结果。初始时,dp[0]为1(表示和为0的情况),其他为0。 -
状态转移:
- 对于每一张卡牌,我们有两个选择:正面或背面。
- 对于每一个选择,更新
dp数组。具体来说,对于当前的dp[i],我们可以通过加上卡牌的正面或背面数字来更新dp[(i + a_i) % 3]和dp[(i + b_i) % 3]。
-
结果:最终,
dp[0]就是所有满足条件的组合数。
总结
通过动态规划,我们可以高效地计算出所有满足条件的组合数。关键在于如何更新 dp 数组,确保每一步都考虑到所有可能的选择。
好的,让我们详细讨论如何使用动态规划来解决这个问题。
动态规划思路
定义状态:
我们使用一个长度为3的数组 dp,其中 dp[i] 表示当前所有可能的和模3等于 i 的组合数。
初始化:
初始时,dp[0] = 1,表示和为0的情况有一种(即什么都不选),其他 dp[1] 和 dp[2] 初始为0。
状态转移:
对于每一张卡牌,我们有两个选择:正面或背面。
对于每一个选择,更新 dp 数组。具体来说,对于当前的 dp[i],我们可以通过加上卡牌的正面或背面数字来更新 dp[(i + a_i) % 3] 和 dp[(i + b_i) % 3]。
结果:
最终,dp[0] 就是所有满足条件的组合数。
代码框架
python
def solution(n: int, a: list, b: list) -> int: MOD = 10**9 + 7 # 初始化dp数组 dp = [0] * 3 dp[0] = 1 # 遍历每一张卡牌 for i in range(n): # 创建一个新的dp数组来 存储更新后的结果 new_dp = [0] * 3 # 更新dp数组 for j in range(3): if dp[j] > 0: new_dp[(j + a[i]) % 3] = (new_dp [(j + a[i]) % 3] + dp [j]) % MOD new_dp[(j + b[i]) % 3] = (new_dp [(j + b[i]) % 3] + dp [j]) % MOD # 将new_dp赋值给dp dp = new_dp # 返回结果 return dp[0]if name == 'main': print(solution(n = 3, a = [1, 2, 3], b = [2, 3, 2]) == 3) print(solution(n = 4, a = [3, 1, 2, 4], b = [1, 2, 3, 1]) == 6) print(solution(n = 5, a = [1, 2, 3, 4, 5], b = [1, 2, 3, 4, 5]) == 32)
关键步骤解释 初始化: dp = [0] * 3 初始化一个长度为3的数组,dp[0] = 1 表示和为0的情况有一种。 状态转移: 对于每一张卡牌,我们创建一个新的 new_dp 数组来存储更新后的结果。 对于每一个可能的和模3的结果 j,我们更新 new_dp[(j + a[i]) % 3] 和 new_dp[(j + b[i]) % 3]。 结果: 最终,dp[0] 就是所有满足条件的组合数。