问题描述
小M有 n 张卡牌,每张卡牌的正反面分别写着不同的数字,正面是 ai,背面是 bi。小M希望通过选择每张卡牌的一面,使得所有向上的数字之和可以被3整除。你需要告诉小M,一共有多少种不同的方案可以满足这个条件。由于可能的方案数量过大,结果需要对 取模。
例如:如果有3张卡牌,正反面数字分别为 (1,2),(2,3) 和 (3,2),你需要找到所有满足这3张卡牌正面或背面朝上的数字之和可以被3整除的组合数。
解题思路
算法分析
这个问题可以通过动态规划来解决。我们定义一个二维数组dp,其中dp[i][j]表示前i张卡牌中,和模3余j的方案数。
步骤1:初始化dp数组
我们初始化dp数组的第一行,dp[0][0]=1,因为当没有卡牌时,总和模3余0的方案数为1。
步骤2:遍历每张卡牌
对于每张卡牌,我们考虑两种情况:选择正面或选择背面。
步骤3:更新dp数组
对于每张卡牌,我们遍历所有可能的余数(0, 1, 2),并根据当前卡牌的正面或背面数字更新dp数组。具体来说,如果当前卡牌的正面数字是front,背面数字是back,那么:
- 如果前i-1张卡牌的和模3余j,那么加上当前卡牌的正面,总和模3的余数将是(j + front) % 3。
- 同样,如果前i-1张卡牌的和模3余j,那么加上当前卡牌的背面,总和模3的余数将是(j + back) % 3。
我们将这两种情况的方案数加到dp[i][(j + front) % 3]和dp[i][(j + back) % 3]上,并取模10^9+7。
步骤4:计算最终结果
最后,我们返回dp[n][0],即前n张卡牌中,和模3余0的方案数。
代码示例
def solution(n: int, a: list, b: list) -> int:
# write code here
MOD = 10**9+7
dp = [[0]*3 for i in range(n+1)]
dp[0][0]=1
for i in range(1,n+1):
front = a[i-1]
back = b[i-1]
for j in range(0,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]
复杂度分析
- 时间复杂度:O(n),每张卡牌都需要遍历 3 种余数的状态转移,因此整体的时间复杂度是 O(n),其中 n 为卡牌的数量。
- 空间复杂度:O(n),我们只需要一个大小为 (n+1)×3 的二维数组来保存每个状态的方案数。