问题描述
小M有 nn 张卡牌,每张卡牌的正反面分别写着不同的数字,正面是 aiai,背面是 bibi。小M希望通过选择每张卡牌的一面,使得所有向上的数字之和可以被3整除。你需要告诉小M,一共有多少种不同的方案可以满足这个条件。由于可能的方案数量过大,结果需要对 109+7109+7 取模。
例如:如果有3张卡牌,正反面数字分别为 (1,2),(2,3) 和 (3,2),你需要找到所有满足这3张卡牌正面或背面朝上的数字之和可以被3整除的组合数。
代码实现
def solution(n: int, a: list, b: list) -> int:
MOD = 10**9 + 7
# 初始化 dp 数组
dp = [0] * 3
dp[0] = 1 # 初始状态,空集合时数字和为 0 的方案
for i in range(n):
next_dp = [0] * 3
for j in range(3):
# 更新选择 a[i] 和 b[i] 对应的状态
next_dp[(j + a[i]) % 3] = (next_dp[(j + a[i]) % 3] + dp[j]) % MOD
next_dp[(j + b[i]) % 3] = (next_dp[(j + b[i]) % 3] + dp[j]) % MOD
dp = next_dp # 更新 dp 数组
return dp[0] # 满足数字和 % 3 == 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)
一、题目解析
本题是一道经典的动态规划问题。题目要求找到一种方式,使得选择每张卡牌的一面后,所有向上的数字之和能够被 3 整除,并求出符合条件的方案数。由于卡牌数量可能较多,我们需要高效地计算可能的方案数,并对结果取模。
二、知识总结
- 动态规划思想:动态规划是一种通过保存中间状态来避免重复计算的优化方法。该题的关键在于利用模运算降低状态的复杂度,只记录模 3 的结果。
- 模运算特性:在取模操作中,可以确保数值在范围内循环变化。对于大数问题,及时取模可避免数据溢出。
- 状态转移方程的构造:在构造动态规划的状态转移方程时,需要结合问题的递归关系,这里通过遍历 dp 并利用模运算更新状态。
三、学习建议
对于新手,建议从经典的动态规划问题(如背包问题)入手,逐步积累分析状态和设计递推公式的能力。
四、总结建议
将动态规划学习与 AI 工具结合,通过练习和总结提升编程能力。注重思维逻辑的培养,同时通过复盘优化代码,实现高效学习与进阶成长。