一、题目分析
首先我们从分析问题开始
分组目标:将数组中的数字分为两组,使得:
- (1) 一组数字的和的个位数等于A。
- (2) 另一组数字的和的个位数等于B。
- (3) 特殊情况,其中一组为空,另一组的和的个位数等于A或B。
根据这个目标要求,将步骤简单概括为以下几点:
- (1) 遍历数组中的所有可能分组方式。
- (2) 计算每种分组方式下两组数字的和的个位数。
- (3) 检查是否满足题目的条件。
举个例子
例如,对于数组 [1, 1, 1] 和目标 A = 1,B = 2:
- 分组 1:
- 组1:
[1],组2:[1, 1] - 组1 的和:1(等于 A)
- 组2 的和:2(等于 B)
- 组1:
- 分组 2:
- 组1:
[1, 1],组2:[1] - 组1 的和:2(等于 B)
- 组2 的和:1(等于 A)
- 组1:
- 分组 3:
- 组1:
[1],组2:[1,1] - 组1 的和:1(等于 A)
- 组2 的和:2(等于 B)
- 组1:
二、解答题目
由于这道题在题目筛选中位于位运算分类里,所以先尝试用位运算
定义一个函数:find(n, A, B, array_a)
n:数组array_a的长度。A:目标和的一个数。B:目标和的另一个数。array_a:需要进行分组的数组。
计算组合数:使用位运算tc = 1 << n
其中1 << n 相当于 2^n,表示所有可能的组合数(因为每个元素都有两种选择:加入组A或组B)。
接着进入一个循环来遍历所有组合:for mask in range(tc)其中mask 是一个从 0 到 2^n - 1 的二进制数,每个位上的 0 或 1 表示某个元素是加入组A还是组B。
在遍历中加入一个判断条件if mask & (1 << i):判断 mask 的第 i 位是否为 1。
- 若为
1,则将array_a[i]加入组A。 - 否则,将
array_a[i]加入组B。
for i in range(n):
if mask & (1 << i): # 判断第i位是否为1
sumA += array_a[i]
else:
sumB += array_a[i]
检查分组和的个位数:if sumA % 10 == A and sumB % 10 == B
若组A和组B的和的个位数分别等于 A 和 B,则计数器 count 增加 1
最后返回结果满足条件的分组方式数量,并对结果取模
这下能轻松秒杀?结果超时了。 尝试询问一下 豆包MarsCode AI 知道了也可以使用动态规划来做这道题,于是尝试换一下方法。
使用动态规划
初始化动态规划表:创建一个三维的动态规划表 dp,dp[i][j][k] 表示前 i 个数中,两组数字和的个位数分别为 j 和 k 的组合数。初始状态为 dp[0][0][0] = 1。
接着遍历数组元素:
- 遍历数组
array_a。 - 对每个元素
array_a[i-1],考虑将其加入第一组还是第二组,并更新 dp 表。 - 更新总和
total。
for i in range(1, len(array_a) + 1):
for j in range(10): # 第一组
for k in range(10): # 第二组
dp[i][j][k] += dp[i-1][((j + 10) - array_a[i-1] % 10) % 10][k]
dp[i][j][k] += dp[i-1][j][((k + 10) - array_a[i-1] % 10) % 10]
total += array_a[i-1]
然后处理特殊情况a = 1 if total % 10 == A else 0,另一组也是同理,如果数组的总和total的个位数等于 A 或 B,设置 a 或 b 为 1,表示一组可以为空的情况。
最后返回结果:dp[n][A][B] 加上 a 和 b,即满足条件的划分方式数量。轻松解决。