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

39 阅读3分钟

问题描述

小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 张卡牌,每张卡牌有正反两面,分别标有数字 a[i]b[i],我们可以选择卡牌的正面或反面,使得所有卡牌朝上的数字之和能够被 3 整除。我们需要求出所有满足这个条件的不同选择方案数。

解答思路

这道题目的关键点在于每张卡牌可以选择正面 a[i] 或背面 b[i],从而组成不同的方案。并且对于每种方案,数字的和必须能够被 3 整除。

这个问题可以通过 动态规划 来求解。核心思想是考虑卡牌的选择会影响到当前的和对 3 的余数,最后我们只关心和对 3 的余数为 0 的方案数。

代码分析

  • 初始化dp数组,dp[i][j]表示前i张卡牌中,选择某些卡牌使得它们的和模3等于j的方案数。 不选择任何卡牌时,和为0的方案数为1。
dp = [[0] * 3 for _ in range(n + 1)]
dp[0][0] = 1
  • 遍历每张卡牌。
for i in range(1, n + 1):
        num1 = a[i - 1]
        num2 = b[i - 1]
  • 更新dp数组,包括选择正面的情况和选择背面的情况。
for j in range(3):
            dp[i][j] = (dp[i][j] + dp[i - 1][(j - num1 % 3 + 3) % 3]) % MOD
            dp[i][j] = (dp[i][j] + dp[i - 1][(j - num2 % 3 + 3) % 3]) % MOD
  • 最终答案为dp[n][0],即前n张卡牌中,选择某些卡牌使得它们的和模3等于0的方案数。
return dp[n][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):
        num1 = a[i - 1]
        num2 = b[i - 1]
        
        for j in range(3):
            dp[i][j] = (dp[i][j] + dp[i - 1][(j - num1 % 3 + 3) % 3]) % MOD
            dp[i][j] = (dp[i][j] + dp[i - 1][(j - num2 % 3 + 3) % 3]) % MOD
    
    return dp[n][0]

总结

时间复杂度:对于每张卡牌,我们遍历了 3 个可能的余数(0、1、2),因此每张卡牌的处理需要 O(3) 的时间。总的时间复杂度是 O(n),其中 n 是卡牌的数量。

空间复杂度:需要维护一个大小为 3 的 dp 数组,因此空间复杂度是 O(1),即常数空间。

这道题的核心是通过动态规划计算所有合法的卡牌选择方案数,其中每个选择对结果的影响仅限于更新当前的余数状态。通过巧妙地设计状态转移,可以高效地解决问题,并且用取模操作确保结果不超过指定范围。