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

52 阅读2分钟

问题描述

原题:卡牌翻面问题

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

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

思路分析

要解决这个问题,我们可以使用动态规划(Dynamic Programming, DP)的方法。我们需要维护一个二维数组 dp[i][j],其中 i 表示当前处理到的卡牌数量,j 表示当前数字之和对3取模的结果。dp[i][j] 表示前 i 张卡牌中选择某些卡牌的正面或背面,使得这些卡牌的数字之和对3取模等于 j 的方案数。

具体步骤如下:

  1. 初始化 dp 数组,dp[0][0] = 1,表示没有卡牌时,数字之和为0的方案数为1。
  2. 遍历每张卡牌,对于每张卡牌的正反面数字,更新 dp 数组。
  3. 最终结果为 dp[n][0],表示前 n 张卡牌中选择某些卡牌的正面或背面,使得这些卡牌的数字之和对3取模等于0的方案数。

代码实现

#include <iostream>
#include <string>
#include <vector>


using namespace std;
const int MOD = 1e9 + 7;
int solution(int n, std::vector<int> a, std::vector<int> b) {
  // write code here
  vector<vector<int>> dp(n + 1, vector<int>(3, 0));
  dp[0][0] = 1;
  for (int i = 1; i < n + 1; i++) {
    for (int j = 0; j < 3; j++) {
      dp[i][(j + a[i - 1]) % 3] =
          (dp[i][(j + a[i - 1]) % 3] + dp[i - 1][j]) % MOD;
      dp[i][(j + b[i - 1]) % 3] =
          (dp[i][(j + b[i - 1]) % 3] + dp[i - 1][j]) % MOD;
    }
  }
  return dp[n][0]; // Placeholder return
}

这个代码首先定义了一个常量 MOD 用于取模运算。函数内部使用动态规划的方法来更新 dp 数组,最后返回 dp[n][0] 作为结果。

本题中,使用动态规划解决并不难想到,关键在于dp数组的构造,i 表示当前处理到的卡牌数量,j 表示当前数字之和对3取模的结果为j,其中j的含义是很有创见的。

动态规划在此类问题中非常高效,因为它避免了暴力求解的指数级复杂性,通过逐步累加状态来得到最终结果。