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

73 阅读4分钟

题目解析

问题描述

小M有 (n) 张卡牌,每张卡牌的正反面分别写着不同的数字,正面是 (a_i),背面是 (b_i)。小M希望通过选择每张卡牌的一面,使得所有向上的数字之和可以被 3 整除。你需要告诉小M,一共有多少种不同的方案可以满足这个条件。由于可能的方案数量过大,结果需要对 (10^9+7) 取模。

我们可以通过组合每张卡牌正反面朝上的数字来实现目标。我们需要找到满足条件的所有组合数,具体是如何选择正反面使得总和能被 3 整除。

思路解析

  1. 动态规划(DP)

    • 我们可以使用动态规划来解决这个问题。设 (dp[r]) 表示当前数字的和除以 3 的余数是 (r) 的方案数。这里 (r) 的取值为 0, 1, 2,表示当前和对 3 取余后可能的三种余数。
  2. 状态转移

    • 对于每张卡牌,我们可以选择它的正面或者背面。正面和背面的数字分别是 (a_i) 和 (b_i),我们需要根据当前的和的余数来计算新的状态。
    • 假设当前余数为 (r),对于正面朝上的卡牌,我们更新下一个状态为 ((r + a_i) % 3),对于背面朝上的卡牌,我们更新下一个状态为 ((r + b_i) % 3)。
  3. 初始化

    • 初始化时,只有 (dp[0] = 1),表示初始状态为数字和为 0(即符合条件的初始状态),而 (dp[1]) 和 (dp[2]) 为 0,表示初始时不可能有余数为 1 或 2 的情况。
  4. 更新

    • 对每张卡牌,更新 (dp) 数组,以确保计算所有可能的组合。

代码详解

def solution(n: int, a: list, b: list) -> int:
    MOD = 10**9 + 7  # 结果需要对 10^9+7 取模
    
    # 初始化 DP 数组,只有 dp[0] = 1,其余为 0
    dp = [1, 0, 0]  # dp[i] 表示当前和对 3 取余为 i 的方案数
    
    # 遍历每张卡牌
    for i in range(n):
        next_dp = [0] * 3  # 用来存储更新后的状态
        for r in range(3):
            # 更新正面朝上的情况
            next_dp[(r + a[i]) % 3] += dp[r]
            next_dp[(r + a[i]) % 3] %= MOD  # 保证取模
            # 更新背面朝上的情况
            next_dp[(r + b[i]) % 3] += dp[r]
            next_dp[(r + b[i]) % 3] %= MOD  # 保证取模
        dp = next_dp  # 更新 dp 数组为新状态
    
    # 最终结果是 dp[0],表示所有方案中,数字和对 3 取余为 0 的方案数
    return dp[0]

代码解析

  1. 初始化dp = [1, 0, 0] 表示初始时,和为 0 的情况有 1 种,和为 1 和 2 的情况没有。

  2. 循环遍历

    • for i in range(n):遍历每一张卡牌。
    • next_dp = [0] * 3:创建一个新的数组 next_dp 来存储更新后的状态。
  3. 状态转移

    • 对于当前状态 (r)(即当前的和对 3 取余的结果),我们尝试选择每张卡牌的正面或背面:
      • next_dp[(r + a[i]) % 3] += dp[r]:表示当前状态为 (r),选择正面 (a_i) 后的新状态是 ((r + a[i]) % 3)。
      • next_dp[(r + b[i]) % 3] += dp[r]:表示当前状态为 (r),选择背面 (b_i) 后的新状态是 ((r + b[i]) % 3)。
  4. 更新 dpdp = next_dp 表示将 dp 数组更新为新的状态数组。

  5. 结果返回return dp[0] 表示最终和对 3 取余为 0 的方案数。

知识总结

  1. 动态规划(DP):本题是一个典型的动态规划问题,通过状态转移来求解问题。利用 DP 技巧,我们能够通过状态压缩和重复计算的消除,显著提高计算效率。

  2. 取模运算:在题目中要求结果对 (10^9+7) 取模,这是为了防止数值溢出,并且也是常见的编程竞赛中的常规要求。使用取模操作,能够保证程序在大数情况下依然能够稳定运行。

  3. 问题建模与解法:通过分析问题,将其转化为一个动态规划问题,并设计出合理的状态转移方程,从而高效地求解。