题目解析
给定一个数组 a,我们需要将数组中的数字分为两组,满足以下要求:
- 一组数字和的个位数等于 A(1 ≤ A ≤ 9)。
- 另一组数字和的个位数等于 B(1 ≤ B ≤ 9)。
或者
- 其中一组为空,剩余数字和的个位数等于 A 或 B。
目标是求出所有满足上述条件的分组方式的数量。
解决思路
这个问题可以通过 动态规划(DP)来解决,核心思想是:我们关心的是数字的和的 个位数,而不是具体的数字本身。所以,我们可以使用 个位数的和 来构建状态,并通过 DP 来判断是否存在满足条件的分组。
分析过程
-
数字的个位数:首先,我们只关心每个数字的个位数。因为题目要求的是数字和的个位数,因此我们对每个数字取个位数,然后进行处理。
-
状态设计:设定一个 DP 状态
dp[i][j],表示使用前i个数字能否得到和的个位数为j。我们一开始已经知道,和为 0 是可行的,因为我们可以选择空集。 -
状态转移:
- 对于每个数字
num,我们尝试将它加入已有的和中,然后更新新的状态。如果当前和为j,加入数字num后新的和的个位数为(j + num) % 10。
- 对于每个数字
-
目标:我们需要检查是否可以通过将数组中的元素分成两组,使得一组和的个位数为 A,另一组和的个位数为 B,或者某一组为空,剩下的数字和的个位数为 A 或 B。
特殊情况
- 如果某一组为空,那么另一组的和的个位数必须等于 A 或 B。
解法步骤
-
计算所有数字的个位数:首先,我们对每个数字取个位数,将其转换为
nums[i] % 10。 -
动态规划求解:
- 设定一个数组
dp,大小为 10,表示当前能达到的和的个位数(从 0 到 9)。 - 初始时,
dp[0] = 1,表示和为 0 是可行的(空集合)。 - 对于每个数字
num,更新dp数组中的每个状态。
- 设定一个数组
-
检查是否满足条件:
- 判断
dp[A]和dp[B],如果都为真,则表示可以分为两组,满足题意。 - 如果只有一个组为空,检查其和的个位数是否为 A 或 B。
- 判断
动态规划实现
pythonCopy Code
def countWays(nums, A, B):
# 获取所有数字的个位数
nums = [num % 10 for num in nums]
# dp[i] 表示是否可以得到和为 i 的个位数
dp = [0] * 10
dp[0] = 1 # 和为 0 是可能的,表示空集
# 动态规划更新 dp
for num in nums:
# 必须从后往前更新,避免重复计算
new_dp = dp[:]
for j in range(10):
new_dp[(j + num) % 10] += dp[j]
dp = new_dp
# 总和的个位数
total_sum = sum(nums) % 10
# 如果 dp[A] 或 dp[B] 为 1,表示我们可以满足某一组和为 A 或 B
result = dp[A] + dp[B]
# 如果 total_sum 为 A 或 B,说明可以把一组设为空,剩余的和满足条件
if total_sum == A or total_sum == B:
result += 1
return result
# 示例
nums = [1, 1, 1]
A = 1
B = 2
print(countWays(nums, A, B)) # 输出可能的分组方式数
代码解析
-
初始化:
- 我们先将所有的数字转换为个位数(通过
% 10操作)。 - 初始化
dp[0] = 1,表示和为 0 是可行的(空集合)。
- 我们先将所有的数字转换为个位数(通过
-
动态规划:
- 我们通过循环遍历
nums数组中的每个数字,并更新dp数组。注意要从后向前更新dp,这样可以避免重复计算。
- 我们通过循环遍历
-
检查满足条件:
- 如果
dp[A]或dp[B]为真,说明有一种分法满足条件。 - 如果整个数组的和的个位数是 A 或 B,可以考虑一组为空的特殊情况。
- 如果
-
返回结果:
- 返回
dp[A] + dp[B],表示有多少种满足条件的分法。 - 如果数组总和的个位数是 A 或 B,额外加上 1 作为一种特殊情况(即另一组为空)。
- 返回
时间复杂度与空间复杂度
- 时间复杂度:
O(n * 10),其中n是数组的长度,10是因为我们只关心个位数(0-9)。 - 空间复杂度:
O(10),因为我们只需要一个大小为 10 的数组来存储状态。
总结
通过动态规划,我们能够有效地计算出满足题目要求的所有分组方式。核心思想是通过个位数来减少状态空间,并通过状态转移来判断是否能够找到符合条件的分组方式。