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

47 阅读3分钟

1.题目

问题描述

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

2.思路

动态规划

  • 状态定义

dp[i][j] 表示前 i 张卡牌中,选择某些卡牌使得它们的数字和模 3 等于 j 的方案数。

  • 状态转移: 对于第 i 张卡牌,其正反面数字为 a[i-1]b[i-1]

    • 如果选择正面,数字之和模 3 的更新公式为: dp[i][(j+a[i−1])%3]+=dp[i−1][j]
    • 如果选择背面,数字之和模 3 的更新公式为: dp[i][(j+b[i−1])%3]+=dp[i−1][j]

dp[i-1][j]:前 i-1 张卡牌中余数为 j 的方案数。假设取正面,余数变为(j+a[i−1])%3

状态 (j + a[i]) % 3(j + b[i]) % 3 都可能被多个路径触及。为了记录所有可能路径的方案数,我们需要对它们进行累加,故使用 +=

计算时,需要对 10^9 + 7 取模。

dp[i][(j + a[i]) % 3] = (dp[i][(j + a[i]) % 3] + dp[i-1][j]) % MOD;
dp[i][(j + b[i]) % 3] = (dp[i][(j + b[i]) % 3] + dp[i-1][j]) % MOD;

初始状态

  • dp[0][0] = 1,表示前 0 张卡牌和为 0 的方案数为 1。
  • 其他状态 dp[0][1] = dp[0][2] = 0

结果计算

  • dp[n][0] 即为答案。

示例:

假设有 2 张卡牌,正反面数字为:

  • 卡牌 1:a[1] = 1, b[1] = 2
  • 卡牌 2:a[2] = 3, b[2] = 2

初始状态:

dp[0][0] = 1 (没有选卡牌,余数为 0)
dp[0][1] = dp[0][2] = 0

处理第 1 张卡牌:

选择 a[1] = 1:
    dp[1][(0 + 1) % 3] = dp[1][1] + dp[0][0] = 0 + 1 = 1
选择 b[1] = 2:
    dp[1][(0 + 2) % 3] = dp[1][2] + dp[0][0] = 0 + 1 = 1

结果:
dp[1][0] = 0
dp[1][1] = 1
dp[1][2] = 1

3.代码

我的代码

public class Main {
    public static int solution(int n, int[] a, int[] b) {
        // write code here
        final int MOD = 1000000007;
        int[][] dp = new int[n + 1][3];
        dp[0][0] = 1;
        //第1张牌到第n张牌
        for (int i = 1; i <= n; i++){
            //正面
            int front = a[i-1];
            //反面
            int back = b[i-1];
            for(int j = 0; j < 3;j++){
                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;
            }
        }
            return dp[n][0]; // Placeholder return
    }

    public static void main(String[] args) {
        System.out.println(solution(3, new int[] { 1, 2, 3 }, new int[] { 2, 3, 2 }) == 3);
        System.out.println(solution(4, new int[] { 3, 1, 2, 4 }, new int[] { 1, 2, 3, 1 }) == 6);
        System.out.println(solution(5, new int[] { 1, 2, 3, 4, 5 }, new int[] { 1, 2, 3, 4, 5 }) == 32);
    }
}

参考代码

public static int solution(int n, int[] a, int[] b)
{
    final int MOD = 1000000007;
    int[][] dp = new int[n + 1][3];

    // 初始化
    dp[0][0] = 1; // 前0张卡牌和为0的方案数为1

    // 动态规划
    for (int i = 1; i <= n; i++)
    {
        int front = a[i - 1]; // 当前卡牌正面数字
        int back = b[i - 1];  // 当前卡牌背面数字
        for (int j = 0; j < 3; j++)
        {
            // 转移方程:选择正面或背面
            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;
        }
    }

    return dp[n][0];
}

4.参考资料

卡牌翻面求和问题 | 豆包MarsCode AI刷题问题描述 小M有 n 张卡牌,每张卡牌的正反面分别写着不同的数字 a - 掘金