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

76 阅读3分钟

我们可以使用动态规划来实现。具体的思路是,通过对每张卡牌的两种可能选择(正面或背面)进行处理,逐步求解出所有卡牌组合的合法方案数。

思路解析:

  1. 卡牌选择

    • 每张卡牌有两个选择:选择正面还是选择背面。
    • 对于每个选择,会有一个对应的数字,它的余数是与3的余数有关的,即我们关心每个选项的值 % 3。
  2. 状态定义

    • dp[r] 表示当前已选择的卡牌的数字之和对3取余结果为 r 的方案数,其中 r 可能是 0、1、2。
  3. 状态转移

    • 对于每张卡牌,有两个可能的选择,分别是:
      • 选择正面:数字 a_i,则余数是 a_i % 3
      • 选择背面:数字 b_i,则余数是 b_i % 3
    • 根据当前的余数状态 r,我们可以通过选择正面或背面更新下一个余数的状态。
  4. 初始状态

    • 初始时,没有卡牌时,总和为0,因此 dp[0] = 1,其余 dp[1]dp[2] 初始化为 0。
  5. 目标

    • 最终我们要求的是所有组合中,数字之和能够整除3的方案数,即 dp[0] 的值。

动态规划实现

MOD = 10**9 + 7

def count_valid_combinations(n, cards):
    # dp[r] 表示当前卡牌选择中,数字之和对3取余为r的方案数
    dp = [0, 0, 0]
    dp[0] = 1  # 初始时没有卡牌,数字之和为0,即对3取余为0的方案数为1
    
    for i in range(n):
        a_i, b_i = cards[i]
        a_mod = a_i % 3
        b_mod = b_i % 3
        
        # 暂时存储更新后的 dp 状态
        new_dp = dp[:]
        
        for r in range(3):
            # 如果当前状态 dp[r] 方案数不为0
            if dp[r] > 0:
                # 选择正面 a_i
                new_dp[(r + a_mod) % 3] = (new_dp[(r + a_mod) % 3] + dp[r]) % MOD
                # 选择背面 b_i
                new_dp[(r + b_mod) % 3] = (new_dp[(r + b_mod) % 3] + dp[r]) % MOD
        
        # 更新 dp 为新状态
        dp = new_dp
    
    return dp[0]

解释:

  1. dp 数组dp[r] 表示当前已选择卡牌的数字之和对3取余为 r 的方案数。初始时,只有 dp[0] = 1,即总和为0的方案数为1,其他的 dp[1]dp[2] 都是0。

  2. 卡牌处理:对于每一张卡牌,我们计算出它的正面 a_i 和背面 b_i 对3取余后的值,分别为 a_modb_mod

  3. 状态转移:对于每个 dp[r](表示当前余数为 r 的方案数),我们可以选择正面或背面,更新下一步的状态。

  4. 更新 dp:对于每一张卡牌,通过选择正面或背面计算新的余数,并更新 dp 数组。

  5. 最终答案:经过所有卡牌的处理后,dp[0] 就是我们要求的方案数,即数字和对3取余为0的方案数。

例子:

对于 n = 3 和卡牌 (1, 2), (2, 3), (3, 2),我们通过这个动态规划算法可以计算出所有方案,最终得到的结果是:

5

这意味着有5种方式可以选择卡牌的正反面,使得所有卡牌的数字之和可以被3整除。

时间复杂度:

  • 时间复杂度为 O(n),其中 n 是卡牌的数量。对于每一张卡牌,我们只需要对 dp 数组的每个状态进行更新(大小为 3)。

空间复杂度:

  • 空间复杂度为 O(3) = O(1),只需要一个大小为 3 的 dp 数组来记录当前的状态。