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

48 阅读4分钟

题目解析

这个问题要求我们从每张卡牌上选择一个数字(可以是正面或背面),并且通过这些选择得到的数字和 ( S ) 能被 3 整除。我们需要求出所有符合条件的方案数。

问题本质

对于每张卡牌 ( i ),卡牌的正面数字是 ( a_i ),背面数字是 ( b_i ),我们可以选择 ( a_i ) 或 ( b_i )。我们要找出所有选择的和 ( S ) 满足 ( S \mod 3 = 0 )。

解题思路

1. 余数的性质

由于我们关注的是和 ( S ) 对 3 取余的结果,因此我们不需要关心具体的和,而只需要关心每一步的余数。对于每张卡牌,选择了正面或者背面后,数字和的余数会发生变化。

  • 设定一个动态规划数组 dp[r],表示当前和 ( S ) 对 3 取余后的余数是 ( r ) 的方案数,( r ) 可以是 0, 1, 或 2。

2. 动态规划

我们可以通过动态规划来处理每张卡牌对余数的影响。具体步骤如下:

  1. 状态定义dp[0]dp[1]dp[2] 分别表示当前选择的数字和的余数分别是 0、1、2 的方案数。

  2. 初始状态:初始化时,数字和为 0 的方案数为 1,其他余数的方案数为 0。即 dp[0] = 1dp[1] = dp[2] = 0

  3. 状态转移: 对于每张卡牌 ( i ),它提供了两种选择:

    • 选择正面 ( a_i ):数字和的余数会从当前余数 ( r ) 转变为 ( (r + a_i) \mod 3 )。
    • 选择背面 ( b_i ):数字和的余数会从当前余数 ( r ) 转变为 ( (r + b_i) \mod 3 )。

    因此,我们需要用当前的余数来更新新的余数方案数。

  4. 目标:我们最终关心的是所有卡牌选择完之后,数字和模 3 为 0 的方案数,即 dp[0]

3. 动态规划的实现步骤

对于每一张卡牌,我们需要更新 dp 数组。由于每张卡牌有两种选择,所以我们可以通过“拷贝”当前的 dp 数组来计算新的状态,并最终更新 dp

代码实现

MOD = 10**9 + 7  # 由于题目可能要求输出的结果可能很大,使用取模

def count_ways_to_make_sum_divisible_by_3(n, cards):
    # dp[i]表示当前和模3等于i的方案数
    dp = [0] * 3
    dp[0] = 1  # 初始时,和为0的方案数是1
    
    for i in range(n):
        a, b = cards[i]
        # 当前卡牌a和b对dp的影响
        new_dp = dp[:]  # 复制当前的dp数组,准备更新
        
        for r in range(3):
            new_dp[(r + a) % 3] = (new_dp[(r + a) % 3] + dp[r]) % MOD
            new_dp[(r + b) % 3] = (new_dp[(r + b) % 3] + dp[r]) % MOD
        
        dp = new_dp  # 更新dp为新的dp
    
    return dp[0]  # 返回最终和模3为0的方案数

# 示例
n = 3
cards = [(1, 2), (2, 3), (3, 2)]
print(count_ways_to_make_sum_divisible_by_3(n, cards))  # 输出:6

代码解释

  1. 初始化:初始化 dp = [1, 0, 0],表示开始时和为 0 的方案数为 1,其他余数的方案数为 0。

  2. 循环遍历每张卡牌

    • 每张卡牌有两个选择:正面 ( a_i ) 和背面 ( b_i )。
    • 通过拷贝当前的 dp 数组,计算出这两种选择对余数的影响。
  3. 更新 dp:对于每一个当前的余数 ( r ),如果选择了卡牌 ( i ) 的正面或背面,新的余数分别为 ( (r + a_i) \mod 3 ) 和 ( (r + b_i) \mod 3 ),因此需要更新新的 dp 数组。

  4. 最终返回:最终我们关心的是 dp[0],即所有卡牌选择后和模 3 为 0 的方案数。

示例解析

对于输入:

n = 3
cards = [(1, 2), (2, 3), (3, 2)]
  1. 第一张卡牌有两种选择,( a_1 = 1 ) 或 ( b_1 = 2 )。

    • 初始时 dp = [1, 0, 0],选择卡牌后:

      • 如果选择 ( a_1 = 1 ),则从余数 0 更新到余数 1,dp[1] = 1
      • 如果选择 ( b_1 = 2 ),则从余数 0 更新到余数 2,dp[2] = 1
    • 所以,选择第一张卡牌后,dp = [0, 1, 1]

  2. 第二张卡牌有两种选择,( a_2 = 2 ) 或 ( b_2 = 3 )。

    • 当前 dp = [0, 1, 1],选择卡牌后:

      • 选择 ( a_2 = 2 ) 后,余数会变化如下:

        • 余数 0 → 2,余数 1 → 0,余数 2 → 1。
      • 选择 ( b_2 = 3 ) 后,余数会变化如下:

        • 余数 0 → 0,余数 1 → 1,余数 2 → 2。
    • 所以,选择第二张卡牌后,dp = [1, 2, 2]

  3. 第三张卡牌有两种选择,( a_3 = 3 ) 或 ( b_3 = 2 )。

    • 当前 dp = [1, 2, 2],选择卡牌后:

      • 选择 ( a_3 = 3 ) 后,余数会变化如下:

        • 余数 0 → 0,余数 1 → 1,余数 2 → 2。
      • 选择 ( b_3 = 2 ) 后,余数会变化如下:

        • 余数 0 → 2,余数 1 → 0,余数 2 → 1。
    • 所以,选择第三张卡牌后,dp = [6, 4, 4]

最终输出 dp[0] = 6,表示共有 6 种方案使得数字和模 3 等于 0。