AI刷题实践:卡牌翻面求和问题 | 豆包MarsCode AI刷题

68 阅读3分钟

问题描述

小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到nj从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整除的方案数。

总结算法步骤:

  1. 初始化dp[0][0] = 1,其他dp[0][j] = 0
  2. 对于每张卡牌,更新dp[i][j]
  3. 最终结果为 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];