选题:### 问题描述
小M有 nn 张卡牌,每张卡牌的正反面分别写着不同的数字,正面是 aiai,背面是 bibi。小M希望通过选择每张卡牌的一面,使得所有向上的数字之和可以被3整除。你需要告诉小M,一共有多少种不同的方案可以满足这个条件。由于可能的方案数量过大,结果需要对 109+7109+7 取模。
例如:如果有3张卡牌,正反面数字分别为 (1,2),(2,3) 和 (3,2),你需要找到所有满足这3张卡牌正面或背面朝上的数字之和可以被3整除的组合数。
问题理解:好的,让我们详细解释一下这个问题的核心要求。
问题核心
我们需要找到一种方法,使得从 n 张卡牌中选择正面或背面朝上,使得所有选中的数字之和可以被3整除。由于可能的方案数量很大,结果需要对 10^9+7 取模。
详细解释
-
卡牌选择:
- 每张卡牌有两个面,正面和背面,分别写着不同的数字。
- 你需要从每张卡牌中选择一个面(正面或背面),使得所有选中的数字之和可以被3整除。
-
数字之和:
- 假设你选择了第
i张卡牌的正面数字a_i,或者背面数字b_i。 - 你需要确保所有选中的数字之和
sum(selected_numbers)可以被3整除,即sum(selected_numbers) % 3 == 0。
- 假设你选择了第
-
方案数量:
- 由于可能的方案数量很大,结果需要对
10^9+7取模。 - 这意味着你需要计算所有可能的方案数,并将结果对
10^9+7取模,以避免整数溢出。
- 由于可能的方案数量很大,结果需要对
示例解释
让我们通过一个简单的例子来进一步理解:
假设有3张卡牌,正反面数字分别为 (1,2),(2,3) 和 (3,2)。
- 选择
1, 2, 3:和为1 + 2 + 3 = 6,可以被3整除。 - 选择
1, 3, 2:和为1 + 3 + 2 = 6,可以被3整除。 - 选择
2, 2, 3:和为2 + 2 + 3 = 7,不能被3整除。 - 选择
2, 3, 2:和为2 + 3 + 2 = 7,不能被3整除。 - 选择
2, 2, 3:和为2 + 2 + 3 = 7,不能被3整除。 - 选择
2, 3, 2:和为2 + 3 + 2 = 7,不能被3整除。
在这个例子中,只有两种选择(1, 2, 3 和 1, 3, 2)满足条件,因此结果为2。
总结
我们需要找到所有可能的组合,使得所有选中的数字之和可以被3整除,并将结果对 10^9+7 取模。
数据结构选择
由于我们需要计算所有可能的组合,并且需要考虑每张卡牌的正面和背面,因此可以使用递归或动态规划来解决这个问题。
算法步骤
-
递归思路:
- 定义一个递归函数
count_ways(index, remainder),其中index表示当前处理的卡牌索引,remainder表示当前选择的数字之和对3的余数。 - 如果
index等于n,表示已经处理完所有卡牌,此时如果remainder为0,则表示找到了一种有效方案,返回1,否则返回0。 - 否则,递归调用
count_ways处理下一张卡牌,分别考虑当前卡牌的正面和背面,并更新remainder。 - 最终返回所有有效方案的数量,并对
10^9+7取模。
- 定义一个递归函数
-
动态规划思路:
- 定义一个二维数组
dp[index][remainder],其中index表示当前处理的卡牌索引,remainder表示当前选择的数字之和对3的余数。 dp[index][remainder]表示从第index张卡牌开始,使得数字之和对3的余数为remainder的方案数。- 初始化
dp[n][0] = 1,表示当处理完所有卡牌时,余数为0的方案数为1。 - 从后向前填充
dp数组,根据当前卡牌的正面和背面更新dp值。 - 最终返回
dp[0][0],表示从第一张卡牌开始,使得数字之和对3的余数为0的方案数。
- 定义一个二维数组
代码框架
def solution(n, a, b):
def count_ways
(index, remainder)
:
# 如果已经处理完
所有卡牌
if index == n:
return 1
if
remainder
== 0 else
0
# 递归处理下一张
卡牌,分别考虑当
前卡牌的正面和背
面
ways = 0
ways +=
count_ways
(index + 1,
(remainder + a
[index]) % 3)
ways +=
count_ways
(index + 1,
(remainder + b
[index]) % 3)
# 返回结果并对
10^9+7 取模
return ways %
(10**9 + 7)
# 从第一张卡牌开始,
初始余数为0
return count_ways
(0, 0) 感悟:
- 递归方法可能会导致重复计算,可以使用记忆化搜索(memoization)来优化。
- 动态规划方法可以避免重复计算,并且通常比递归方法更高效。