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

54 阅读3分钟

问题描述

小M有 n 张卡牌,每张卡牌的正反面分别写着不同的数字。正面数字为 a[i],背面数字为 b[i]。小M希望通过选择每张卡牌的一面,使得所有卡牌正面或背面朝上的数字之和可以被 3 整除。你需要告诉小M,一共有多少种不同的方案可以满足这个条件。由于可能的方案数量过大,结果需要对 109+7109+7 取模。

解题思路

  1. 状态转移
    这道题是一个典型的动态规划问题,我们可以通过状态转移来解决。设 dp[i][j] 表示前 i 张卡牌选择后,数字和对 3 取余结果为 j 的方案数。目标是计算出当所有卡牌选择完后,数字和对 3 取余结果为 0 的方案数。

  2. 状态初始化
    初始时没有卡牌时,数字和为 0,所以 dp[0][0] = 1,其他状态为 0。

  3. 状态转移
    对于每张卡牌,我们有两种选择:选择卡牌的正面或选择背面。通过对这两种选择的数字和对 3 取余结果进行更新,来更新状态。

    • 对于卡牌的正面数字 a[i],我们可以通过 dp[i-1][(j - a[i] % 3) % 3] 转移。
    • 对于卡牌的背面数字 b[i],我们可以通过 dp[i-1][(j - b[i] % 3) % 3] 转移。
  4. 取模操作
    由于可能的方案数过大,我们每次计算时都要对 109+7109+7 取模。

  5. 最终结果
    我们需要返回的是 dp[n][0],即所有卡牌选择完成后,数字和对 3 取余为 0 的方案数。

代码实现

def solution(n: int, a: list, b: list) -> int:
    MOD = 10**9 + 7
    dp = [[0] * 3 for _ in range(n + 1)]
    dp[0][0] = 1  
    
    for i in range(1, n + 1):
        a_mod = a[i - 1] % 3
        b_mod = b[i - 1] % 3
        
        for j in range(3):
            dp[i][j] = (dp[i - 1][(j - a_mod) % 3] + dp[i - 1][(j - b_mod) % 3]) % MOD
    return dp[n][0]

复杂度分析

  • 时间复杂度

    • 初始化 dp 数组需要 O(n) 的时间。
    • 动态规划状态转移时,对于每张卡牌和每种取余情况,我们都需要进行状态转移,因此时间复杂度为 O(n * 3)。

    综合时间复杂度为 O(n),由于每次状态转移只涉及常数次计算。

  • 空间复杂度

    • dp 数组的大小为 (n + 1) * 3,因此空间复杂度为 O(n)。

测试用例

用例 1
输入:

n = 3, a = [1, 2, 3], b = [2, 3, 2]

输出:

3

解释:有 3 张卡牌,正反面数字分别为 (1, 2)(2, 3)(3, 2)。所有满足和对 3 取余为 0 的方案有 3 种。

用例 2
输入:

复制代码
n = 4, a = [3, 1, 2, 4], b = [1, 2, 3, 1]

输出:

6

解释:有 4 张卡牌,正反面数字分别为 (3, 1)(1, 2)(2, 3)(4, 1)。总共有 6 种方案可以使得数字和对 3 取余为 0。

用例 3
输入:

n = 5, a = [1, 2, 3, 4, 5], b = [1, 2, 3, 4, 5]

输出:

32

解释:有 5 张卡牌,正反面数字分别为 (1, 1)(2, 2)(3, 3)(4, 4)(5, 5)。总共有 32 种方案可以使得数字和对 3 取余为 0。

总结

这道题的关键在于:

  • 使用动态规划来表示每一步的选择。
  • 将每张卡牌的选择结果转化为对 3 的余数,减少了复杂度。
  • 利用状态转移来不断更新方案数,最终得到符合条件的方案数。