《卡牌翻面求和问题》、《魔法甜点之和:小包的新挑战》 | 豆包MarsCode AI刷题

47 阅读5分钟

一、卡牌翻面求和问题

问题理解

小M有 n 张卡牌,每张卡牌的正反面分别写着不同的数字,正面是 a[i],背面是 b[i]。小M希望通过选择每张卡牌的一面,使得所有向上的数字之和可以被 3 整除。你需要计算有多少种不同的方案可以满足这个条件。由于可能的方案数量过大,结果需要对 10^9 + 7 取模。

示例:

  • 输入:n = 3a = [1, 2, 3]b = [2, 3, 2]
  • 输出:3
  • 解释:有3种方案使得和可以被3整除。
  • 分别是:a[0]a[1]a[2]a[0]b[1]b[2]b[0]a[1]b[2]

数据结构选择

  • 动态规划(DP)数组:用来记录到每张卡牌时,选择某些卡牌使得和模 3 余 j的方案数。定义 dp[i][j] 表示前 i 张卡牌中,选择某些卡牌使得和模 3 余 j 的方案数。
  • 整数变量:用于计数和索引操作。

算法步骤

  1. 初始化

    • 定义 MOD = 10^9 + 7,用于结果取模。
    • 初始化 DP 数组 dp,大小为 (n + 1) x 3,所有值设为 0
    • 初始状态 dp[0][0] = 1,表示不选任何卡牌时,和为 0 的方案数为 1
  2. 动态规划填表

    • 遍历每张卡牌 i

      • 获取当前卡牌的正面和背面的数字 num1 = a[i-1] 和 num2 = b[i-1]

      • 对于每个余数 j0 到 2):

        • 更新选择正面的情况:dp[i][(j + num1) % 3] = (dp[i][(j + num1) % 3] + dp[i - 1][j]) % MOD
        • 更新选择背面的情况:dp[i][(j + num2) % 3] = (dp[i][(j + num2) % 3] + dp[i - 1][j]) % MOD
  3. 返回结果

    • 最终答案为 dp[n][0],表示前 n 张卡牌中,选择某些卡牌使得和模 3 余 0 的方案数。

代码实现

def solution(n: int, a: list, b: list) -> int:

    MOD = 10**9 + 7

    # 初始化dp数组,dp[i][j]表示前i张卡牌中,选择某些卡牌使得和模3余j的方案数
    dp = [[0] * 3 for _ in range(n + 1)]
    dp[0][0] = 1  # 初始状态,不选任何卡牌时,和为0的方案数为1

    for i in range(1, n + 1):
        num1 = a[i - 1]
        num2 = b[i - 1]

        # 更新dp数组
        for j in range(3):
            # 选择正面的情况
            dp[i][(j + num1) % 3] = (dp[i][(j + num1) % 3] + dp[i - 1][j]) % MOD
            # 选择背面的情况
            dp[i][(j + num2) % 3] = (dp[i][(j + num2) % 3] + dp[i - 1][j]) % MOD

    # 最终答案为dp[n][0],表示前n张卡牌中,选择某些卡牌使得和模3余0的方案数
    return dp[n][0]

二、魔法甜点之和:小包的新挑战

问题理解

小R有 N 个甜点,每个甜点都有一个固定的喜爱值。小R有 M 个魔法棒,可以选择任意甜点使用,但每个甜点只能使用一次魔法棒。使用魔法棒可以将甜点的喜爱值变为原来的阶乘。小R的目标是通过选择一些甜点,可能使用魔法棒,使得这些甜点的喜爱值之和恰好为 S。需要计算有多少种不同的方案满足这个要求。如果两种方案中,选择的甜点不同,或者使用魔法棒的甜点不同,则视为不同的方案。

数据结构选择

  • 动态规划(DP)数组:用来记录到每个甜点时,选择某些甜点使得和为 k 的方案数。定义 dp[i][j][k] 表示前 i 个甜点中,使用 j 个魔法棒,使得和为 k 的方案数。
  • 整数变量:用于计数和索引操作。

算法步骤

  1. 初始化

    • 定义 max_s = S + 1,用于方便处理边界。
    • 初始化 DP 数组 dp,大小为 (N + 1) x (M + 1) x max_s,所有值设为 0
    • 初始状态 dp[0][0][0] = 1,表示不选任何甜点时,和为 0 的方案数为 1
  2. 动态规划填表

    • 遍历每个甜点 i

      • 获取当前甜点的喜爱值 current_like = like[i - 1] 和其阶乘 current_factorial = factorial(current_like)

      • 对于每个魔法棒数量 j0 到 M):

        • 对于每个可能的和 k0 到 max_s - 1):

          • 不使用当前甜点:dp[i][j][k] += dp[i - 1][j][k]
          • 使用当前甜点,不使用魔法棒:如果 k >= current_like,则 dp[i][j][k] += dp[i - 1][j][k - current_like]
          • 使用当前甜点,使用魔法棒:如果 j > 0 且 k >= current_factorial,则 dp[i][j][k] += dp[i - 1][j - 1][k - current_factorial]
  3. 计算结果

    • 计算所有使用 M 个魔法棒的方案数:total_ways = sum(dp[N][j][S] for j in range(M + 1))
  4. 返回结果

    • 返回 total_ways,表示满足条件的方案数。

代码实现

def factorial(n):
    if n == 0 or n == 1:
        return 1
    result = 1
    
    for i in range(2, n + 1):
        result *= i

    return result

def solution(n, m, s, like):

    max_s = s + 1  # 为了方便处理边界
    dp = [[[0] * max_s for _ in range(m + 1)] for _ in range(n + 1)]
    dp[0][0][0] = 1  # 初始状态,不选任何甜点时,和为0的方案数为1

    for i in range(1, n + 1):
        current_like = like[i - 1]
        current_factorial = factorial(current_like)
        for j in range(m + 1):
            for k in range(max_s):
                # 不使用当前甜点
                dp[i][j][k] += dp[i - 1][j][k]
                # 使用当前甜点,不使用魔法棒
                if k >= current_like:
                    dp[i][j][k] += dp[i - 1][j][k - current_like]
                # 使用当前甜点,使用魔法棒
                if j > 0 and k >= current_factorial:
                    dp[i][j][k] += dp[i - 1][j - 1][k - current_factorial]

    # 计算所有使用 m 个魔法棒的方案
    total_ways = sum(dp[n][j][s] for j in range(m + 1))

    return total_ways