问题描述
原题:卡牌翻面问题
小M有 nn 张卡牌,每张卡牌的正反面分别写着不同的数字,正面是 aiai,背面是 bibi。小M希望通过选择每张卡牌的一面,使得所有向上的数字之和可以被3整除。你需要告诉小M,一共有多少种不同的方案可以满足这个条件。由于可能的方案数量过大,结果需要对 109+7109+7 取模。
例如:如果有3张卡牌,正反面数字分别为 (1,2),(2,3) 和 (3,2),你需要找到所有满足这3张卡牌正面或背面朝上的数字之和可以被3整除的组合数。
思路分析
要解决这个问题,我们可以使用动态规划(Dynamic Programming, DP)的方法。我们需要维护一个二维数组 dp[i][j],其中 i 表示当前处理到的卡牌数量,j 表示当前数字之和对3取模的结果。dp[i][j] 表示前 i 张卡牌中选择某些卡牌的正面或背面,使得这些卡牌的数字之和对3取模等于 j 的方案数。
具体步骤如下:
- 初始化
dp数组,dp[0][0] = 1,表示没有卡牌时,数字之和为0的方案数为1。 - 遍历每张卡牌,对于每张卡牌的正反面数字,更新
dp数组。 - 最终结果为
dp[n][0],表示前n张卡牌中选择某些卡牌的正面或背面,使得这些卡牌的数字之和对3取模等于0的方案数。
代码实现
#include <iostream>
#include <string>
#include <vector>
using namespace std;
const int MOD = 1e9 + 7;
int solution(int n, std::vector<int> a, std::vector<int> b) {
// write code here
vector<vector<int>> dp(n + 1, vector<int>(3, 0));
dp[0][0] = 1;
for (int i = 1; i < n + 1; i++) {
for (int j = 0; j < 3; j++) {
dp[i][(j + a[i - 1]) % 3] =
(dp[i][(j + a[i - 1]) % 3] + dp[i - 1][j]) % MOD;
dp[i][(j + b[i - 1]) % 3] =
(dp[i][(j + b[i - 1]) % 3] + dp[i - 1][j]) % MOD;
}
}
return dp[n][0]; // Placeholder return
}
这个代码首先定义了一个常量 MOD 用于取模运算。函数内部使用动态规划的方法来更新 dp 数组,最后返回 dp[n][0] 作为结果。
本题中,使用动态规划解决并不难想到,关键在于dp数组的构造,i 表示当前处理到的卡牌数量,j 表示当前数字之和对3取模的结果为j,其中j的含义是很有创见的。
动态规划在此类问题中非常高效,因为它避免了暴力求解的指数级复杂性,通过逐步累加状态来得到最终结果。