题目描述
小M有 n 张卡牌,每张卡牌的正反面分别写着不同的数字,正面是 ,背面是 。小M希望通过选择每张卡牌的一面,使得所有向上的数字之和可以被3整除。你需要告诉小M,一共有多少种不同的方案可以满足这个条件。由于可能的方案数量过大,结果需要对 取模。
例如:如果有3张卡牌,正反面数字分别为 (1,2),(2,3) 和 (3,2),你需要找到所有满足这3张卡牌正面或背面朝上的数字之和可以被3整除的组合数。
输入
输出
算法
动态规划
动态规划思路
为了方面,下面卡牌的起始位置从0开始。
这道题的核心是使用动态规划来解决组合问题,通过状态转移来累积结果。我们定义两个状态数组 dp1 和 dp2:
dp1[i][j]表示选择第i张卡牌的正面,使得从第0张卡牌到第i张卡牌的组合数字之和的余数为j的方案数量。dp2[i][j]表示选择第i张卡牌的反面,使得从第0张卡牌到第i张卡牌的组合数字之和的余数为j的方案数量。
其中,j的值可以是0, 1, 或 2,因为我们只关心和对3的余数。
状态转移方程
对于每张卡牌,我们要更新其正反两面各自的状态:
-
当前卡牌的正面数字是
a[i],反面数字是b[i]。 -
对于前
i-1张卡牌的每种余数状态j(0, 1, 2),我们有以下转移规则:- 若第
i张卡牌选择正面,其和的余数更新为(a[i] + j) % 3。 - 若第
i张卡牌选择反面,其和的余数更新为(b[i] + j) % 3。
- 若第
因此,状态转移公式为:
dp1[i][(a[i] + j) % 3] += (dp1[i - 1][j] + dp2[i - 1][j]) % mod;
dp1[i][(a[i] + j) % 3] %= mod;
dp2[i][(b[i] + j) % 3] += (dp1[i - 1][j] + dp2[i - 1][j]) % mod;
dp2[i][(b[i] + j) % 3] %= mod;
-
解释:
dp1[i][(a[i] + j) % 3]:当前选择第i张卡牌的正面,那么和的余数将变为(a[i] + j) % 3。我们从前一张卡牌的正反面余数状态dp1[i-1][j]和dp2[i-1][j]转移而来,因此累加它们的方案数。dp2[i][(b[i] + j) % 3]:当前选择第i张卡牌的反面,那么和的余数将变为(b[i] + j) % 3,从前一张卡牌的正反面余数状态dp1[i-1][j]和dp2[i-1][j]转移而来。
初始化
对于第0张卡牌,我们直接初始化dp1和dp2的初始状态:
dp1[0][a[0] % 3] = 1表示若选择第0张卡片的正面,其余数状态为a[0] % 3。dp2[0][b[0] % 3] = 1表示若选择第0张卡片的反面,其余数状态为b[0] % 3。
终止条件和结果提取
遍历完所有卡片后,我们得到dp1[n-1][0] 和 dp2[n-1][0],分别表示在选择所有卡片的正反面时,和能被3整除的方案数。最终答案为它们之和:
(dp1[n - 1][0] + dp2[n - 1][0]) % mod;
复杂度分析
- 时间复杂度:
O(n * 3),对于每张卡片,我们更新3种余数状态(0, 1, 2),因此总复杂度是O(3 * n) ≈ O(n)。 - 空间复杂度:
O(n * 3 * 2),因为我们有两个n * 3的状态数组dp1和dp2。
总结
这道题的难点在于:
- 卡牌正反面选择带来的双状态设计,即需要分别考虑正反两种选择路径。
- 余数状态的动态规划转移,确保每步的和对3的余数被正确累积。
- 取模处理,由于组合数可能非常大,每次累加都需要进行取模操作以避免溢出。
通过上述动态规划方法,我们能在时间复杂度为O(n)的情况下有效地求解方案数。
代码
constexpr int mod = 1e9 + 7;
int solution(int n, std::vector<int> a, std::vector<int> b) {
// write code here
std::vector<std::vector<int> > dp1(n, std::vector<int>(3, 0));
std::vector<std::vector<int> > dp2(n, std::vector<int>(3, 0));
dp1[0][a[0] % 3] = 1;
dp2[0][b[0] % 3] = 1;
for (int i = 1; i < n; i ++) {
a[i] %= 3;
b[i] %= 3;
for (int j = 0; j < 3; j ++) {
dp1[i][(a[i] + j) % 3] += (dp1[i - 1][j] + dp2[i - 1][j]) % mod;
dp1[i][(a[i] + j) % 3] %= mod;
dp2[i][(b[i] + j) % 3] += (dp1[i - 1][j] + dp2[i - 1][j]) % mod;
dp2[i][(b[i] + j) % 3] %= mod;
}
}
return (dp1[n - 1][0] + dp2[n - 1][0]) % mod; // Placeholder return
}