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

2 阅读3分钟

问题描述

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

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

问题分析

这道题的核心在于一个组合选择问题:对于每张卡牌,我们需要在正面或反面之间做出选择,从而使得所有朝上的数字之和满足整除3的条件。这种问题通常涉及到有限状态的枚举和动态规划的技巧应用。

我们需要逐步分析每张卡牌的两种可能性(正面或反面朝上),并维护能够被3整除的情况数。

具体分析如下:

  1. 状态定义
    定义一个二维DP数组 dp[i][j]:表示前 i 张卡牌中,选择某些面向上的数字总和模3后等于 j 的方案数。
    其中:

    • i 表示前 i 张卡牌。
    • j 的取值范围是 0,1,2,分别表示模3余数为0、1或2的情况。
  2. 状态转移方程
    对于每张卡牌 i,有两种选择:

    • 正面朝上,向总和中添加数字 a[i−1]。
    • 反面朝上,向总和中添加数字 b[i−1]。

    根据当前总和模3的结果(mod),更新下一张卡牌的状态:

    dp[i][(mod+a[i−1])%3]+=dp[i−1][mod]

    dp[i][(mod+b[i−1])%3]+=dp[i−1][mod]

    注意,每次累加后需要对 10^9+7 取模,避免溢出。

  3. 初始化
    边界条件是没有卡牌时的初始状态:

    dp[0][0]=1

    这表示没有卡牌时,数字总和为0模3的方案数为1。

  4. 结果计算
    最终答案是 dp[n][0],表示前 n 张卡牌中,总和模3等于0的方案数。

算法实现

以下是Python实现代码:

def count_ways_to_divisible_by_3(n, a, b):
    MOD = 1e9 + 7
    
    # 初始化DP数组
    dp = [[0] * 3 for _ in range(n + 1)]
    dp[0][0] = 1  # 初始状态

    for i in range(1, n + 1):
        for mod in range(3):
            # 更新当前卡牌正面选择后的状态
            dp[i][(mod + a[i-1]) % 3] += dp[i-1][mod]
            dp[i][(mod + a[i-1]) % 3] %= MOD

            # 更新当前卡牌反面选择后的状态
            dp[i][(mod + b[i-1]) % 3] += dp[i-1][mod]
            dp[i][(mod + b[i-1]) % 3] %= MOD

    return dp[n][0]

复杂度分析

  1. 时间复杂度
    外层循环遍历 n 张卡牌,内层循环遍历模3的3种状态,总体复杂度为 O(3×n)=O(n)。

  2. 空间复杂度
    使用了一个大小为 n×3 的DP数组,因此空间复杂度为 O(3×n)=O(n)。