“题目解析#33 | 豆包MarsCode AI 刷题”

119 阅读3分钟

在这个问题中,小M面临一个有趣的挑战:在一组卡牌上选择每张卡牌的一个面(正面或背面),使得所有选择数字的和能够被3整除。为了解决这个问题,我们需要深入分析题目要求,提出解决思路,并通过代码实现该算法。以下是对问题的详细解析。

问题背景

给定 n 张卡牌,每张卡牌的正面和背面分别有不同的数字,如 a[i] 表示正面数字,b[i] 表示背面数字。我们需要遍历所有可能的组合,并找出那些组合其数字和能够被3整除的方案。由于可能的组合数量很大,我们将在结果上取模 10^9 + 7

思路分析

  1. 组合的性质

    • 每张卡牌可以有两种状态(正面或背面),因此,对于 n 张卡牌总共有 2^n 种选择组合。
    • 由于我们只关心和是否能够被3整除,所以可以利用模运算简化问题。我们只需考虑每张卡牌的数字对3取余后的结果。
  2. 动态规划

    • 使用动态规划(Dynamic Programming, DP)可以有效解决这个组合问题。设定一个 DP 数组 dp[i][j],其中 i 表示考虑到第 i 张卡牌,j 表示和 mod 3 的结果。
    • 初始化条件为没有卡牌时的和为0,即 dp[0][0] = 1
  3. 状态转移

    • 对于第 i 张卡牌,考虑将其正面(值为 a[i-1])或背面(值为 b[i-1])朝上:

      • 如果选正面,更新状态 dp[i][(j + ai) % 3]
      • 如果选背面,更新状态 dp[i][(j + bi) % 3]
    • 这个转移过程会涉及到前一状态的所有 j 值(和的余数)。

通过以上的思路,我们可以编写出代码来实现这个逻辑。以下是实现代码:

def solution(n: int, a: list, b: list) -> int:
MOD = 10**9 + 7    
    dp = [[0] * 3 for _ in range(n + 1)]
    dp[0][0] = 1
# 初始情况下,没有卡牌,和为0的情况
for i in range(1, n + 1):
    ai = a[i - 1] % 3
    bi = b[i - 1] % 3
    for j in range(3):
        dp[i][(j + ai) % 3] = (dp[i][(j + ai) % 3] + dp[i - 1][j]) % MOD
        dp[i][(j + bi) % 3] = (dp[i][(j + bi) % 3] + dp[i - 1][j]) % MOD

return dp[n][0]

代码详解

  1. 常量定义

    • MOD 常量用于防止大数溢出,确保结果在有效范围内。
  2. DP 数组的初始化

    • dp 数组定义为 n + 1 行和3列以便存储每步的结果。
  3. 状态更新逻辑

    • 外层循环遍历每张卡牌:

      • 内层循环遍历每种可能的和的余数,并对其进行更新。
      • 通过模运算对更新后的状态结果进行规约,以保证有效性。

总结

这个问题展示了动态规划的强大之处。通过将问题拆解为子问题,利用以前计算的结果来构建更复杂的逻辑,我们得以有效解决问题。设计好的与条件关于模数的状态转移保证了计算的效率。正是通过这样的分析和实现,我们能够在面对复杂的组合问题时找到高效的解法。希望通过这篇解析,大家能更好地理解动态规划的应用,以及如何利用数据的性质简化问题的复杂度。