问题描述
小M有 张卡牌,每张卡牌的正反面分别写着不同的数字,正面是 ,背面是 。小M希望通过选择每张卡牌的一面,使得所有向上的数字之和可以被3整除。你需要告诉小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
在这些例子中,我们要找到满足卡牌正面或背面朝上的组合,使得它们的和可以被3整除的方案数量。
解题思路
这道题的核心问题是:如何通过选择卡牌的一面,使得所有朝上的数字之和被3整除?
由于我们要确定被3整除的条件,这意味着我们只需关心每张卡牌数字对3取模的结果。具体来说:
- 每张卡牌的正面或背面数字可能对3取余结果是0、1或2。
- 因此,我们可以使用模3的余数来更新方案数。
步骤1:模3分类
对于每张卡牌,我们可以分别计算它的正反面数字模3后的结果。这样,我们只需要记录和追踪这些模3余数的组合数即可。
步骤2:动态规划
我们使用动态规划的方法来求解这个问题。通过动态规划数组 dp[i][j] 表示前 i 张卡牌中选择若干张卡牌的一面,使得这些数字之和对3取余数等于 j 的方案数。
其中 j 的取值范围是0到2(因为我们只关心是否能被3整除),具体定义为:
dp[i][0]表示前i张卡牌中可以得到的所有和中被3整除的方案数。dp[i][1]表示前i张卡牌中可以得到的所有和中对3余1的方案数。dp[i][2]表示前i张卡牌中可以得到的所有和中对3余2的方案数。
步骤3:转移方程
对于每张卡牌的正面数字 a_i 和背面数字 b_i,我们可以基于前一张卡牌的结果来更新当前卡牌的方案数:
- 如果选择正面
a_i,则会将dp[i-1][k]更新到dp[i][(k + a_i) % 3]。 - 如果选择背面
b_i,则会将dp[i-1][k]更新到dp[i][(k + b_i) % 3]。
我们对每一张卡牌进行上述更新,直到遍历完所有卡牌。
步骤4:初始化
对于 dp[0][0],我们初始化为1,因为在没有选择任何卡牌的情况下,我们的和为0,并且0可以被3整除,这是一种有效方案。其他状态初始化为0。
步骤5:取模
为了避免方案数过大导致的溢出问题,每次更新 dp 时都需要对结果取模 (10^9+7)。
步骤6:返回结果
最后,我们需要的结果是 dp[n][0],即从所有卡牌中选择若干张,使得和能够被3整除的方案数。
Python代码实现
以下是根据上述思路实现的Python代码:
MOD = 10**9 + 7
def solution(n: int, a: list, b: list) -> int:
# dp[i][j] 表示前 i 张卡牌中,选择若干张后数字和模 3 等于 j 的方案数
dp = [[0] * 3 for _ in range(n + 1)]
dp[0][0] = 1 # 初始化
for i in range(1, n + 1):
# 当前卡牌的正反面数字对3取模后的值
mod_a = a[i - 1] % 3
mod_b = b[i - 1] % 3
# 临时存储 dp[i-1] 的拷贝,以便更新 dp[i] 时不覆盖
new_dp = [0] * 3
for j in range(3):
# 选择 a_i
new_dp[(j + mod_a) % 3] = (new_dp[(j + mod_a) % 3] + dp[i - 1][j]) % MOD
# 选择 b_i
new_dp[(j + mod_b) % 3] = (new_dp[(j + mod_b) % 3] + dp[i - 1][j]) % MOD
# 更新 dp[i]
dp[i] = new_dp
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)
复杂度分析
- 时间复杂度:(O(n)),因为我们只需遍历一次卡牌数组,每张卡牌进行3次余数状态的更新。
- 空间复杂度:(O(3n)),因为我们只存储每张卡牌的模3状态。
总结
希望本博客能帮助你更好地理解动态规划,解决更多组合类问题!