第四篇|卡牌翻面求和|豆包MarsCode AI技术刷题

35 阅读5分钟

一、问题描述

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

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


测试样例

样例1:

输入:n = 3 ,a = [1, 2, 3] ,b = [2, 3, 2]
输出:3

样例2:

输入:n = 4 ,a = [3, 1, 2, 4] ,b = [1, 2, 3, 1]
输出:6

样例3:

输入:n = 5 ,a = [1, 2, 3, 4, 5] ,b = [1, 2, 3, 4, 5]
输出:32

二、代码展示

MOD = 10 ** 9 + 7

def solution(n, a, b):

    dp = [[0] * 3 for _ in range(n + 1)]

    dp[0][0] = 1

    for i in range(1, n + 1):

        for j in range(3):

            dp[i][j] += dp[i - 1][(j - a[i - 1]) % 3]

            dp[i][j] += dp[i - 1][(j - b[i - 1]) % 3]

            dp[i][j] %= MOD

    return dp[n][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)

三、代码解析

1.整体功能概述

这段Python代码定义了一个函数  solution ,其目的是根据给定的输入  n 、列表  a  和列表  b ,通过动态规划的方式计算并返回一个特定的结果。从代码最后的测试部分可以看出,对于不同的  n 、 a  和  b  的取值组合,期望得到相应的计算结果并进行验证。

2.代码细节分析

(1)定义常量  MOD
  MOD = 10 ** 9 + 7

这里定义了一个常量  MOD ,它的值为  1000000007 。在后续的计算中,会经常对计算结果取模(即求余数)操作,使用这个常量来确保计算结果在一个特定的范围内,避免整数溢出等问题,这在处理一些可能涉及较大数值的计算场景中是很常见的做法。

(2)函数  solution  的定义
  def solution(n, a, b):
    dp = [[0] * 3 for _ in range(n + 1)]
    dp[0][0] = 1

    for i in range(1, n + 1):
        for j in range(3):
            dp[i][j] += dp[i - 1][(j - a[i - 1]) % 3]
            dp[i][j] += dp[i - 1][(j - b[i - 1]) % 3]
            dp[i][j] %= MOD

    return dp[n][0]
(3)初始化动态规划数组  dp :
  •  dp = [[0] * 3 for _ in range(n + 1)] :创建了一个二维列表  dp ,它的第一维大小为  n + 1 ,第二维大小为  3 。这个数组将用于存储中间计算结果,通过动态规划的思想逐步构建最终要返回的结果。
  •  dp[0][0] = 1 :初始化  dp  数组的左上角元素为  1 ,这通常是动态规划问题中的初始状态设定,为后续的递推计算提供了一个起始点。
动态规划递推计算过程:
  • 外层循环  for i in range(1, n + 1) :按照从  1  到  n  的顺序逐步遍历,每一次循环都在基于上一步的结果计算当前步的  dp  值,这符合动态规划从子问题逐步构建到整个问题的解决思路。
  • 内层循环  for j in range(3) :对于每一个  i  值,都要对  j  从  0  到  2  进行遍历。这里的  j  取值范围为  3 ,可能与问题本身的某种状态划分相关,比如可能代表了三种不同的情况或者状态。
在每次内层循环中:
  •  dp[i][j] += dp[i - 1][(j - a[i - 1]) % 3] :这一步是根据动态规划的递推关系,将上一步(即  i - 1  时)的某个相关状态的值累加到当前状态  dp[i][j]  中。具体来说,是通过  (j - a[i - 1]) % 3  来索引到上一步的某个特定位置的值,这里取模操作  % 3  可能与前面提到的三种状态相关,确保索引值在  0  到  2  的范围内。
  •  dp[i][j] += dp[i - 1][(j - b[i - 1]) % 3] :与上一步类似,只不过这里是根据列表  b  中的值来索引上一步的另一个相关位置的值,并累加到当前的  dp[i][j] 。
  •  dp[i][j] %= MOD :在每次累加完上一步的相关值后,立即对结果进行取模操作,保证  dp[i][j]  的值始终在  0  到  MOD - 1  的范围内,防止数值过大导致溢出等问题。
  • 返回最终结果:  return dp[n][0] :在完成了所有的动态规划递推计算后,返回  dp  数组中最后一行(即  n  行)第一列(即  0  列)的元素值,这个值就是根据给定的输入  n 、 a  和  b  通过前面的计算过程得到的最终结果。

3.主程序部分

  
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)

  这里是主程序部分,当脚本直接运行(而不是被作为模块导入到其他脚本中)时,会执行这部分代码。它通过调用  solution  函数并传入不同的参数组合  n 、 a  和  b ,然后将函数返回值与预期的结果进行比较,并打印出比较的结果(是否相等)。这样可以方便地对  solution  函数进行测试,验证其对于不同输入是否能正确计算出预期的结果。

综上所述,这段代码通过动态规划的方法,根据给定的输入参数  n 、 a  和  b ,逐步计算并最终返回一个特定的结果,同时在主程序部分对函数的正确性进行了简单的测试验证。