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

135 阅读5分钟

题目解析:卡牌翻面求和问题

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

例如:如果有3张卡牌,正反面数字分别为 (1,2)(2,3) 和 (3,2),你需要找到所有满足这3张卡牌正面或背面朝上的数字之和可以被3整除的组合数。

测试样例

样例1:

输入:n = 3 ,a = [1, 2, 3] ,b = [2, 3, 2]
输出:3

样例2:

输入:n = 4 ,a = [3, 1, 2, 4] ,b = [1, 2, 3, 1]
输出:6

样例3:

输入:n = 5 ,a = [1, 2, 3, 4, 5] ,b = [1, 2, 3, 4, 5]
输出:32

题目背景

我们有 n 张卡牌,每张卡牌有两个数字,分别来自数组 ab。每次我们从两张卡牌中选择一个数字。我们需要计算选出的卡牌数字和的余数为 0 的组合方式的个数,即选出的卡牌和除以 3 的余数为 0 的方案数。

解题思路

  1. 动态规划状态定义

    • 定义一个 dp 数组,其中 dp[r] 表示选取若干张卡牌后的所有数字和除以 3 的余数为 r 的方案数。这里的 r 取值为 0, 1, 2,表示余数为 0、1、2 的情况。
    • 初始时,dp[0] = 1,因为没有选择任何卡牌时,和为 0,显然能被 3 整除;dp[1] = 0 和 dp[2] = 0,表示没有选择任何卡牌时,余数为 1 或 2 的情况不成立。
  2. 状态转移

    • 对于每张卡牌 i,有两种选择:要么选择 a[i],要么选择 b[i]。每次选择时,我们都会更新每个余数的情况。
    • 具体来说,假设当前 dp[r] 存储的是以余数 r 结尾的组合数目,如果我们选择 a[i],新的余数变为 (r + a[i]) % 3,相应地,我们将 dp[r] 的值加到 new_dp[(r + a[i]) % 3] 上。同理,如果选择 b[i],我们会更新 new_dp[(r + b[i]) % 3]

    这种方式通过遍历所有的卡牌,逐步累积并更新每个余数的可能组合。

  3. 最终结果

    • 所有卡牌都选择完之后,dp[0] 即为最终的答案,因为我们要求的是所有可能的选择方式中,和除以 3 的余数为 0 的方案数。

代码解析

pythonCopy Code
def solution(n: int, a: list, b: list) -> int:
    MOD = 10**9 + 7  # 用来处理大数,防止溢出

    # 初始化动态规划数组
    dp = [0, 0, 0]
    dp[0] = 1  # 初始时,和为0的方案数为1

    for i in range(n):
        # 计算当前卡牌选择后的新方案数
        new_dp = [0, 0, 0]
        
        for r in range(3):
            # 对于每个余数r,可以选择a[i]或b[i]
            new_dp[(r + a[i]) % 3] = (new_dp[(r + a[i]) % 3] + dp[r]) % MOD
            new_dp[(r + b[i]) % 3] = (new_dp[(r + b[i]) % 3] + dp[r]) % MOD
        
        # 更新dp数组
        dp = new_dp

    # dp[0]就是我们需要的方案数
    return dp[0]

详细解释

  1. 初始化 dp 数组

    • dp[0] = 1:表示在没有选卡牌的情况下,数字和为 0,可以被 3 整除。
    • dp[1] = 0 和 dp[2] = 0:表示没有选卡牌时,和不能被 3 整除,初始时这两者不可能。
  2. 动态更新 dp

    • 对于每一张卡牌,我们通过遍历当前所有可能的余数 r,计算选择当前卡牌 a[i] 或 b[i] 后可能的新余数,并更新到 new_dp 数组中。
    • new_dp[(r + a[i]) % 3] 表示选择 a[i] 后的余数为 (r + a[i]) % 3,并将 dp[r] 加到对应的新余数位置上。
    • 同样的操作也应用到 b[i]
  3. 更新状态

    • 遍历完所有卡牌后,dp 就会保存最终所有选卡牌后可能的余数情况。
    • 最终返回 dp[0],即和为 0,能够被 3 整除的组合方式数。
  4. 模运算

    • 由于结果可能会非常大,为了避免溢出,所有的累加操作都使用 MOD = 10**9 + 7 来进行取模。

举例说明

输入 1:

pythonCopy Code
n = 3
a = [1, 2, 3]
b = [2, 3, 2]
  • 选择卡牌后的和的余数为 0 的方案数为 3。

输入 2:

pythonCopy Code
n = 4
a = [3, 1, 2, 4]
b = [1, 2, 3, 1]
  • 选择卡牌后的和的余数为 0 的方案数为 6。

输入 3:

pythonCopy Code
n = 5
a = [1, 2, 3, 4, 5]
b = [1, 2, 3, 4, 5]
  • 选择卡牌后的和的余数为 0 的方案数为 32。

时间复杂度

  • 时间复杂度为 O(n),其中 n 是卡牌的数量。每张卡牌都遍历一次,对于每个卡牌,需要更新 3 个余数的情况,所以每次循环的时间复杂度为 O(3),因此总的时间复杂度为 O(n)

空间复杂度

  • 空间复杂度为 O(3),即 dp 数组的大小固定为 3,用来保存余数为 0、1、2 的方案数。

心得:

使用MarsCode AI编写代码让我体验到了编程的便利与高效。AI能够快速生成代码示例,帮助我理解不同编程概念。通过交互式的反馈,我能迅速调整思路,解决问题。同时,MarsCode AI提供的建议让我了解到更多最佳实践,提升了我的编码水平。这种工具不仅节省了时间,还激发了我的创造力,尤其是在处理复杂问题时,AI的支持显得尤为重要。总的来说,MarsCode AI是编程学习和实践中的得力助手。