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

62 阅读3分钟

题目分析

我们有 n 张卡牌,每张卡牌的正反面分别写有不同的数字。我们可以选择让每张卡牌的正面或反面朝上,从而得到每张卡牌的一个数字。现在,我们需要找到一种选法,使得所有朝上的数字之和能够被 3 整除,并计算出满足这一条件的不同选法的总数。

由于直接计算所有组合的和非常繁琐,我们可以借助动态规划来优化这个问题。

解题思路

1. 余数状态表示

在考虑每种选法时,我们只关心最终和对 3 的余数,因为题目要求最终的和是 3 的倍数。这样我们可以将所有可能的和划分为三种余数状态:

  • 余数为 0 的状态(即满足条件的状态,因为是 3 的倍数)。
  • 余数为 1 的状态。
  • 余数为 2 的状态。

用一个数组 dp 来记录每种余数的状态的方案数,其中:

  • dp[0] 表示所有选法中,使得数字之和对 3 取余为 0 的方案数。
  • dp[1] 表示所有选法中,使得数字之和对 3 取余为 1 的方案数。
  • dp[2] 表示所有选法中,使得数字之和对 3 取余为 2 的方案数。

2. 初始化状态

初始情况下,我们没有选择任何卡牌,此时总和为 0,且方案数为 1。因此,dp[0] = 1 表示没有选择任何卡牌的状态,和为 0。

3. 动态转移

对于每张卡牌,我们可以选择它的正面朝上或者反面朝上。假设这张卡牌的正面数字是 a[i],反面数字是 b[i],对于已有的所有余数状态,我们可以更新为新的余数状态。更新的过程如下:

  • 对于当前余数为 k 的方案,若我们选择卡牌的正面数字 a[i],则新的余数状态为 (k + a[i]) % 3
  • 同样地,若选择卡牌的反面数字 b[i],则新的余数状态为 (k + b[i]) % 3

这样,对于每种当前余数 k,选择正反面分别会影响到不同的余数状态。这些新的余数状态可以累加到对应的 dp 值中,从而得到新的方案数。

4. 累计方案数

在每一步(即处理一张卡牌)后,我们会根据选择更新每种余数的状态的方案数。这样当我们遍历完所有卡牌后,dp[0] 中就记录了使得所有选法中总和是 3 的倍数的方案数。

5. 模运算

由于方案数可能非常大,在每次更新方案数时都要对 10^9 + 7 取模,以确保结果不会溢出。

举例说明

假设有 3 张卡牌,正反面数字分别是 (1, 2), (2, 3), (3, 2)

  1. 初始化

    • 初始状态 dp = [1, 0, 0],表示和为 0 的方案数为 1,没有选择任何卡牌时的初始状态。
  2. 处理第 1 张卡牌 (1, 2)

    • 当前状态为 dp = [1, 0, 0]
    • 如果选择正面 1,余数变为 (0 + 1) % 3 = 1,更新 dp[1] += dp[0]
    • 如果选择反面 2,余数变为 (0 + 2) % 3 = 2,更新 dp[2] += dp[0]
    • 结果:dp = [1, 1, 1]
  3. 处理第 2 张卡牌 (2, 3)

    • 当前状态为 dp = [1, 1, 1]
    • 如果选择正面 2
      • dp[0] 更新为 (dp[0] + dp[1]) % MOD
      • dp[1] 更新为 (dp[1] + dp[2]) % MOD
      • dp[2] 更新为 (dp[2] + dp[0]) % MOD