问题描述
小M有 n 张卡牌,每张卡牌的正反面分别写着不同的数字,正面是 ai,背面是 bi。小M希望通过选择每张卡牌的一面,使得所有向上的数字之和可以被3整除。你需要告诉小M,一共有多少种不同的方案可以满足这个条件。由于可能的方案数量过大,结果需要对 10^9+7 取模。
例如:如果有3张卡牌,正反面数字分别为 (1,2),(2,3) 和 (3,2),你需要找到所有满足这3张卡牌正面或背面朝上的数字之和可以被3整除的组合数。
思路
每张牌有两种取法,正和反,这种计算组合数的题目可以使用动态规划来做。
我们定义状态dp[i][j],其中i表示考虑前i张卡牌时,所有向上的数字之和除以3的余数为j的方案数。我们需要计算的是所有i从1到n,j从0到2的dp[i][j]的总和。
从基础情况开始:
dp[0][0] = 1,表示在没有卡牌时,和为0的方案有1种(即不选择任何卡牌)。dp[0][1] = 0和dp[0][2] = 0,因为没有卡牌时,和不能为1或2。
接下来,我们考虑每张卡牌。对于每张卡牌,我们有两种选择:选择正面或背面。对于第i张卡牌,如果正面是ai,背面是bi,我们需要更新状态dp[i][j]。
状态转移方程为: dp[i][j]=dp[i−1][(j−ai)mod 3]+dp[i−1][(j−bi)mod 3]
dp[i][j]=dp[i−1][(j−ai)mod3]+dp[i−1][(j−bi)mod3]
其中,dp[i-1][(j-a_i) \mod 3]表示选择第i张卡牌的正面,而dp[i-1][(j-b_i) \mod 3]表示选择第i张卡牌的背面。
我们需要对每个状态进行更新,并在每一步中对结果取模10^9+7以避免整数溢出。
最后,我们对所有可能的余数求和: 总方案数=dp[n][0]mod (109+7)总方案数=dp[n][0]mod(109+7)
这是因为我们只关心所有向上的数字之和可以被3整除的方案数。
总结算法步骤:
- 初始化
dp[0][0] = 1,其他dp[0][j] = 0。 - 对于每张卡牌,更新
dp[i][j]。 - 最终结果为
dp[n][0],即前n张卡牌中,选择正面或背面朝上的数字之和模3等于0的方案数。
这就是解决这个问题的基本思路。也是动态规划解决这种多种选择组合问题的基本思路。
代码如下
int MOD = 1000000007;
int[][] dp = new int[n + 1][3];
// 初始化
dp[0][0] = 1;
for (int i = 1; i <= n; 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];