问题描述
小M有 张卡牌,每张卡牌的正反面分别写着不同的数字,正面是 ,背面是 。小M希望通过选择每张卡牌的一面,使得所有向上的数字之和可以被3整除。你需要告诉小M,一共有多少种不同的方案可以满足这个条件。由于可能的方案数量过大,结果需要对 取模。
例如:如果有3张卡牌,正反面数字分别为 (1,2),(2,3) 和 (3,2),你需要找到所有满足这3张卡牌正面或背面朝上的数字之和可以被3整除的组合数。
测试样例
样例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
我们需要找到所有卡牌正面或背面朝上的数字之和可以被3整除的组合数。
例如,假设小M手中有3张卡牌,它们的正反面数字分别为(1,2),(2,3)和(3,2)。那么,小M需要找到所有可能的卡牌选择方案,使得这些卡牌上被选中的数字之和可以被3整除。在这个例子中,存在3种满足条件的方案。
再比如,如果小M手中有4张卡牌,它们的正反面数字分别为(3,1),(1,2),(2,3)和(4,1)。同样地,小M需要找到所有可能的卡牌选择方案,使得这些卡牌上被选中的数字之和可以被3整除。在这个例子中,存在6种满足条件的方案。
类似地,对于其他数量的卡牌和不同的正反面数字组合,小M都需要知道满足条件的方案数量。
要解决这个问题,我们可以使用动态规划(DP)来记录选择某些卡牌使得其和模3的余数等于0、1、2的方案数。具体地,我们可以定义一个二维数组 dp,其中 dp[i][j] 表示前 i 张卡牌中,选择和模3余 j 的方案数。
初始化时,dp[0][0] = 1,表示不选任何卡牌时,和为0的方案数为1。然后,我们遍历每一张卡牌,对于每张卡牌,我们有两个选择:选正面或选背面,并更新 dp 数组。
以下是具体的步骤和代码实现:
- 初始化
dp数组,dp[0][0] = 1。 - 遍历每一张卡牌,对于每张卡牌,计算其正面和背面模3的余数。
- 更新
dp数组,对于每张卡牌,更新所有可能的余数情况。 - 最终结果为
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):
# 当前卡牌的正面和背面的数字
front = a[i - 1]
back = b[i - 1]
# 更新dp数组
for j in range(3):
# 选择正面的情况
dp[i][(j + front) % 3] = (dp[i][(j + front) % 3] + dp[i - 1][j]) % MOD
# 选择背面的情况
dp[i][(j + back) % 3] = (dp[i][(j + back) % 3] + dp[i - 1][j]) % MOD
# 最终答案是前n张卡牌中,和模3余0的方案数
return dp[n][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][0] = 1表示不选任何卡牌时,和为0的方案数为1。 - 遍历卡牌:对于每张卡牌,计算其正面和背面的余数,然后更新
dp数组。 - 更新状态:对于每张卡牌的正面和背面,根据当前余数
dp[i][(j + front) % 3] + dp[i - 1][j]或dp[i][(j + back) % 3] + dp[i - 1][j],更新对应的dp值。 - 取模操作:由于结果可能非常大,我们在每次更新
dp时都要对MOD取模。
通过这种方式,我们可以高效地计算出所有卡牌选择和模3余0的方案数。