学习方法与心得5——卡牌翻面求和问题 | 豆包MarsCode AI刷题

49 阅读2分钟

问题描述

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

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

代码实现

def solution(n: int, a: list, b: list) -> int:
    MOD = 10**9 + 7

    # 初始化 dp 数组
    dp = [0] * 3
    dp[0] = 1  # 初始状态,空集合时数字和为 0 的方案

    for i in range(n):
        next_dp = [0] * 3
        for j in range(3):
            # 更新选择 a[i] 和 b[i] 对应的状态
            next_dp[(j + a[i]) % 3] = (next_dp[(j + a[i]) % 3] + dp[j]) % MOD
            next_dp[(j + b[i]) % 3] = (next_dp[(j + b[i]) % 3] + dp[j]) % MOD
        dp = next_dp  # 更新 dp 数组

    return dp[0]  # 满足数字和 % 3 == 0 的方案数

if __name__ == '__main__':
    print(solution(n=3, a=[1, 2, 3], b=[2, 3, 2]) == 3)
    print(solution(n=4, a=[3, 1, 2, 4], b=[1, 2, 3, 1]) == 6)
    print(solution(n=5, a=[1, 2, 3, 4, 5], b=[1, 2, 3, 4, 5]) == 32)

一、题目解析

本题是一道经典的动态规划问题。题目要求找到一种方式,使得选择每张卡牌的一面后,所有向上的数字之和能够被 3 整除,并求出符合条件的方案数。由于卡牌数量可能较多,我们需要高效地计算可能的方案数,并对结果取模。

二、知识总结

  • 动态规划思想:动态规划是一种通过保存中间状态来避免重复计算的优化方法。该题的关键在于利用模运算降低状态的复杂度,只记录模 3 的结果。
  • 模运算特性:在取模操作中,可以确保数值在范围内循环变化。对于大数问题,及时取模可避免数据溢出。
  • 状态转移方程的构造:在构造动态规划的状态转移方程时,需要结合问题的递归关系,这里通过遍历 dp 并利用模运算更新状态。

三、学习建议

对于新手,建议从经典的动态规划问题(如背包问题)入手,逐步积累分析状态和设计递推公式的能力。

四、总结建议

将动态规划学习与 AI 工具结合,通过练习和总结提升编程能力。注重思维逻辑的培养,同时通过复盘优化代码,实现高效学习与进阶成长。