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

79 阅读4分钟

问题描述

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

我们需要找到所有卡牌正面或背面朝上的数字之和可以被3整除的组合数。

例如,假设小M手中有3张卡牌,它们的正反面数字分别为(1,2),(2,3)和(3,2)。那么,小M需要找到所有可能的卡牌选择方案,使得这些卡牌上被选中的数字之和可以被3整除。在这个例子中,存在3种满足条件的方案。

再比如,如果小M手中有4张卡牌,它们的正反面数字分别为(3,1),(1,2),(2,3)和(4,1)。同样地,小M需要找到所有可能的卡牌选择方案,使得这些卡牌上被选中的数字之和可以被3整除。在这个例子中,存在6种满足条件的方案。

类似地,对于其他数量的卡牌和不同的正反面数字组合,小M都需要知道满足条件的方案数量。

要解决这个问题,我们可以使用动态规划(DP)来记录选择某些卡牌使得其和模3的余数等于0、1、2的方案数。具体地,我们可以定义一个二维数组 dp,其中 dp[i][j] 表示前 i 张卡牌中,选择和模3余 j 的方案数。

初始化时,dp[0][0] = 1,表示不选任何卡牌时,和为0的方案数为1。然后,我们遍历每一张卡牌,对于每张卡牌,我们有两个选择:选正面或选背面,并更新 dp 数组。

以下是具体的步骤和代码实现:

  1. 初始化 dp 数组,dp[0][0] = 1
  2. 遍历每一张卡牌,对于每张卡牌,计算其正面和背面模3的余数。
  3. 更新 dp 数组,对于每张卡牌,更新所有可能的余数情况。
  4. 最终结果为 dp[n][0],即前 n 张卡牌选择和模3余0的方案数。
def solution(n: int, a: list, b: list) -> int:
    MOD = 10**9 + 7
    # 初始化dp数组,dp[i][j]表示前i张卡牌中,选择某些卡牌使得和模3余j的方案数
    dp = [[0] * 3 for _ in range(n + 1)]
    dp[0][0] = 1  # 初始状态,不选任何卡牌时,和为0的方案数为1

    for i in range(1, n + 1):
        # 当前卡牌的正面和背面的数字
        front = a[i - 1]
        back = b[i - 1]
        
        # 更新dp数组
        for j in range(3):
            # 选择正面的情况
            dp[i][(j + front) % 3] = (dp[i][(j + front) % 3] + dp[i - 1][j]) % MOD
            # 选择背面的情况
            dp[i][(j + back) % 3] = (dp[i][(j + back) % 3] + dp[i - 1][j]) % MOD

    # 最终答案是前n张卡牌中,和模3余0的方案数
    return dp[n][0]

if __name__ == '__main__':
    print(solution(n = 3, a = [1, 2, 3], b = [2, 3, 2]) == 3)
    print(solution(n = 4, a = [3, 1, 2, 4], b = [1, 2, 3, 1]) == 6)
    print(solution(n = 5, a = [1, 2, 3, 4, 5], b = [1, 2, 3, 4, 5]) == 32)

解释

  1. 初始化dp[0][0] = 1 表示不选任何卡牌时,和为0的方案数为1。
  2. 遍历卡牌:对于每张卡牌,计算其正面和背面的余数,然后更新 dp 数组。
  3. 更新状态:对于每张卡牌的正面和背面,根据当前余数 dp[i][(j + front) % 3] + dp[i - 1][j] 或 dp[i][(j + back) % 3] + dp[i - 1][j],更新对应的 dp 值。
  4. 取模操作:由于结果可能非常大,我们在每次更新 dp 时都要对 MOD 取模。

通过这种方式,我们可以高效地计算出所有卡牌选择和模3余0的方案数。