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

45 阅读3分钟

1.问题描述

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

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

2.问题解决代码及注释(java)

初始化动态规划数组,dp[i][j][k] 表示前i张卡牌,余数为j,是否选择第i张卡牌正面的方案数

int[][][] dp = new int[n + 1][3][2];
dp[0][0][0] = 1; 

遍历所有可能的余数;选择第i张卡牌的正面,更新余数;选择第i张卡牌的反面,更新余数

for (int i = 1; i <= n; i++) {
            for (int j = 0; j < 3; j++) {
                dp[i][j][1] = (dp[i - 1][(j - a[i - 1] % 3 + 3) % 3][0] + dp[i - 1][(j - a[i - 1] % 3 + 3) % 3][1]) % MOD;
                dp[i][j][0] = (dp[i - 1][(j - b[i - 1] % 3 + 3) % 3][0] + dp[i - 1][(j - b[i - 1] % 3 + 3) % 3][1]) % MOD;
            }
        }

最终结果为所有卡牌都考虑后,余数为0的方案数之和

return (dp[n][0][0] + dp[n][0][1]) % MOD;

3.程序UML类图

UML.png

4.程序数据结构

本段Java代码是一个动态规划算法的实现,用于解决一个特定的问题。程序中定义了一个solution函数,它接受三个参数:n表示卡牌的数量,a数组表示每张卡牌正面的值,b数组表示每张卡牌反面的值。目标是计算出所有可能的卡牌组合的和的方案数,其中每张卡牌可以选择正面朝上或反面朝上。

  1. MOD常量

    • private static final int MOD = 1000000007; 定义了一个常量MOD,用于在计算过程中取模,以避免整数溢出,并符合某些数学问题中对结果模一个特定数的要求。
  2. dp数组

    • int[][][] dp = new int[n + 1][3][2]; 定义了一个三维数组dp,用于存储动态规划的状态。
    • dp[i][j][k]的含义如下:
      • i表示当前考虑的卡牌编号(从1到n)。
      • j表示当前卡牌的和的模3余数(0,1,2)。
      • k表示是否选择了卡牌的正面(0表示反面,1表示正面)。
  3. 初始化

    • dp[0][0][0] = 1; 表示在没有选择任何卡牌时,和为0,且没有选择正面,这是一个基础情况。
  4. 状态转移方程

    • 外层循环for (int i = 1; i <= n; i++)遍历每张卡牌。
    • 中层循环for (int j = 0; j < 3; j++)遍历所有可能的和的模3余数。
    • 内层计算dp[i][j][1]dp[i][j][0]分别表示选择当前卡牌正面和反面时的方案数。
    • dp[i][j][1]的计算涉及到dp[i - 1]的状态,表示上一张卡牌的状态,通过a[i - 1] % 3计算当前卡牌正面值对和的模3余数的影响,并结合上一张卡牌的状态来更新当前状态。
    • dp[i][j][0]的计算方式类似,但是使用b[i - 1] % 3来计算反面值的影响。
  5. 最终结果

    • return (dp[n][0][0] + dp[n][0][1]) % MOD; 表示最终结果,即所有可能的卡牌组合的和的方案数,包括正面和反面的情况。