刷题笔记|青训营X豆包MarsCode 技术训练营

51 阅读4分钟

卡牌反面求和:

问题描述

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

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

  1. 卡牌信息:题目给出了小M拥有的n张卡牌,每张卡牌有正反面,正面数字为a_i,背面数字为b_i
  2. 目标要求:要通过选择每张卡牌的一面,使得所有向上的数字之和能被3整除,并且求出满足此条件的不同方案数量,结果需对10^9 + 7取模。

二、解题思路分析

  1. 动态规划思路: - 定义状态:设dp[i][j]表示考虑前i张卡牌,所有向上数字之和除以3的余数为j的方案数。 - 状态转移方程: - 对于第i张卡牌,有两种选择,选正面或选背面。 - 如果选正面,即a[i - 1]朝上,那么dp[i][j] += dp[i - 1][(j - a[i - 1] % 3 + 3) % 3]。这里对a[i - 1]取模是为了保证计算得到的余数在0到2之间,加上3再取模是为了处理可能出现的负数余数情况。 - 如果选背面,即b[i - 1]朝上,那么dp[i][j] += dp[i - 1][(j - b[i - 1] % 3 + 3) % 3]。 - 初始化:dp[0][0] = 1,表示不选任何卡牌时,数字之和为0,余数为0的方案数为1。其他dp[0][j]j为1和2)初始化为0。
  2. 计算顺序: - 按照i从1到nj从0到2的顺序依次计算dp[i][j]
  3. 最终结果: - 最终要求的满足条件的方案数就是dp[n][0],即考虑完所有n张卡牌后,数字之和除以3余数为0的方案数。由于结果要对10^9 + 7取模,所以最后返回dp[n][0] % (10^9 + 7)

三、测试样例分析

  1. 样例1: - 输入:n = 3a = [1, 2, 3]b = [2, 3, 2]。 - 按照动态规划的计算过程: - 初始化dp[0][0] = 1dp[0][1] = dp[0][2] = 0。 - 当i = 1时: - 若选a[0] = 1朝上,dp[1][(0 - 1 % 3 + 3) % 3] = dp[1][2] += dp[0][0] = 1。 - 若选b[0] = 2朝上,dp[1][(0 - 2 % 3 + 3) % 3] = dp[1][1] += dp[0][0] = 1。 - 当i = 2时: - 若选a[1] = 2朝上,dp[2][(0 - 2 % 3 + 3) % 3] = dp[2][1] += dp[1][(0 - 2 % 3 + 3) % 3] = dp[1][1] = 1。 - 若选b[1] = 3朝上,dp[2][(0 - 3 % 3 + 3) % 3] = dp[2][0] += dp[1][(0 - 3 % 3 + 3) % 3] = dp[1][0] = 1。 - 当i = 3时: - 若选a[2] = 3朝上,dp[3][(0 - 3 % 3 + 3) % 3] = dp[3][0] += dp[2][(0 - 3 % 3 + 3) % 3] = dp[2][0] = 2。 - 若选b[2] = 2朝上,dp[3][(0 - 2 % 3 + 3) % 3] = dp[3][1] += dp[2][(0 - 2 % 3 + 3) % 3] = dp[2][1] = 2。 - 最终dp[3][0] = 3,输出结果为3,符合预期。
  2. 样例2: - 输入:n = 4a = [3, 1, 2, 4]b = [1, 2, 3, 1]。 - 类似样例1的分析过程,经过动态规划的计算,最终得到dp[4][0] = 6,输出结果为6,符合预期。
  3. 样例3: - 输入:n = 5a = [1, 2, 3, 4, 5]b = [1, 2, 3, 4, 5]。 - 通过动态规划计算,最终得到dp[5][0] = 32,输出结果为32,符合预期。

五、总结感悟 1. 动态规划的应用:本题通过定义合适的状态和状态转移方程,运用动态规划的思想有效地解决了问题。这让我体会到动态规划在处理具有多个阶段决策且各阶段之间存在关联的问题时的强大作用,能够通过逐步计算保存中间结果,避免重复计算,从而高效地求出最终结果。

  1. 取模运算的处理:由于结果需要对10^9 + 7取模,在整个计算过程中都要注意对中间结果进行取模运算,以保证最终结果的正确性。这提醒我在涉及到取模运算的问题中,要严格按照取模的规则进行计算,避免因数据溢出等问题导致错误的结果。
  2. 问题分析与状态定义:准确地分析问题并定义合适的状态是解决本题的关键。通过将问题转化为考虑每张卡牌选择后数字之和除以3的余数情况,并以此定义状态,能够清晰地构建出状态转移方程,进而实现代码的正确编写。这表明在解决复杂问题时,深入分析问题、找到合适的问题表述方式和状态定义对于设计有效的算法至关重要。