AI刷题33. 卡牌翻面求和问题解析
问题描述
小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
解题思路
1. 理解问题
我们需要找到所有可能的卡牌组合,使得这些组合的数字之和可以被3整除。由于每张卡牌有正反两面,因此每张卡牌有两种选择。问题的核心在于如何高效地计算所有可能的组合,并且确保这些组合的和可以被3整除。
2. 动态规划的引入
为了高效地计算所有的组合,我们可以使用动态规划(Dynamic Programming, DP)。我们需要一个三维的DP数组 dp[i][j][k],其中:
i表示前i张卡牌j表示当前和模3的结果k表示选择卡牌的数量
3. 初始化DP数组
我们初始化 dp[0][0][0] = 1,表示初始状态(没有选择任何卡牌)时,和模3的结果为0,且选择卡牌的数量为0。
4. 状态转移方程
我们需要通过递推公式来填充DP数组。对于每一张卡牌,我们有两种选择:使用正面或使用背面。我们通过以下公式进行状态转移:
- 使用正面:
dp[i][(j + a[i-1]) % 3][k+1] += dp[i-1][j][k] - 使用背面:
dp[i][(j + b[i-1]) % 3][k+1] += dp[i-1][j][k]
这里,i 表示当前处理的卡牌编号,j 表示当前和模3的结果,k 表示已经选择的卡牌数量。
5. 计算最终结果
最终结果是所有 dp[n][0][k] 的和,其中 n 是卡牌总数,k 是选择的卡牌数量,0 表示和模3的结果为0。由于结果可能非常大,我们需要对 109+7109+7 取模。
6. 代码实现
以下是完整的代码实现:
def solution(n: int, a: list, b: list) -> int:
MOD = 10**9 + 7
# 初始化dp数组
dp = [[[0 for _ in range(n+1)] for _ in range(3)] for _ in range(n+1)]
dp[0][0][0] = 1
# 动态规划
for i in range(1, n+1):
for j in range(3):
for k in range(i):
# 使用正面
dp[i][(j+a[i-1])%3][k+1] = (dp[i][(j+a[i-1])%3][k+1] + dp[i-1][j][k]) % MOD
# 使用背面
dp[i][(j+b[i-1])%3][k+1] = (dp[i][(j+b[i-1])%3][k+1] + dp[i-1][j][k]) % MOD
# 计算最终结果
result = sum(dp[n][0][k] for k in range(1, n+1)) % MOD
return result
if __name__ == '__main__':
print(solution(3, [1, 2, 3], [2, 3, 2]) == 3)
print(solution(4, [3, 1, 2, 4], [1, 2, 3, 1]) == 6)
print(solution(5, [1, 2, 3, 4, 5], [1, 2, 3, 4, 5]) == 32)
7. 代码解释
MOD = 10**9 + 7:定义模数。dp = [[[0 for _ in range(n+1)] for _ in range(3)] for _ in range(n+1)]:初始化三维DP数组。dp[0][0][0] = 1:初始化初始状态。for i in range(1, n+1):遍历每一张卡牌。for j in range(3):遍历当前和模3的所有可能结果。for k in range(i):遍历已经选择的卡牌数量。dp[i][(j+a[i-1])%3][k+1] = (dp[i][(j+a[i-1])%3][k+1] + dp[i-1][j][k]) % MOD:使用正面的转移方程。dp[i][(j+b[i-1])%3][k+1] = (dp[i][(j+b[i-1])%3][k+1] + dp[i-1][j][k]) % MOD:使用背面的转移方程。result = sum(dp[n][0][k] for k in range(1, n+1)) % MOD:计算最终结果。return result:返回结果。