二分数字组合
问题描述
小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
样例4:
输入:
n = 5,A = 3,B = 7,array_a = [2, 3, 5, 7, 9]
输出:0
解题思路
-
计算数组和:首先计算数组所有元素的和
total_sum,并获取其个位数total_sum % 10。 -
动态规划表:使用一个二维 DP 表
dp[i][j]来表示前i个数字能否分配成和个位数为j的集合。这里的j取值范围是 0 到 9。 -
状态转移:
-
对于每个数字
array[i],我们可以选择将其放入某一组,或者不放入。我们更新 DP 表以反映当前选择的影响。 -
对于
j从 0 到 9,我们有两种选择:- 不选择当前数字,保持之前的状态
dp[i-1][j]。 - 选择当前数字,更新状态为
(j + array[i]) % 10。
- 不选择当前数字,保持之前的状态
-
-
考虑特殊情况:如果允许其中一组为空,我们需要判断
total_sum % 10是否与 A 或 B 相等。 -
结果计算:最后,我们需要统计所有符合条件的划分方式。
动态规划的基本思想 动态规划(Dynamic Programming,DP)算法通常用于求解某种具有最优性质的问题。在这类问题中,可能会有许多可行解,每一个解都对应一个值,我们希望找到具有最优值的解。 动态规划算法与分治法类似,其基本思想也是将待求解的问题分解成若干个子问题,先求解子问题,然后从这些子问题的解中 得到原有问题的解。与分治法不同的是,动态规划经分解后得到的子问题往往 不是相互独立 的。
(分治算法也可以解决分解后得到的子问题不是相互独立的情况,只是要对公共子问题进行单独求解。这样会使分治法求解问题的复杂度大大提高。对此类问题如果感兴趣的话可以看我的另外一篇文章——分治法详细讲解。)
动态规划分为两种:选择 or 加和 注意边界,想明白dp代表的意义
解决代码
def solution(n, A, B, array_a):
total_sum = sum(array_a)
# 动态规划数组,dp[i][j]表示前i个数字组成的和的个位数为j的方式数量
dp = [[0] * 10 for _ in range(n + 1)]
dp[1][array_a[0]%10] = 1
# DP 填充
for i in range(1, n + 1):
num = array_a[i - 1]
for j in range(10):
# 不选择当前数字
dp[i][j] += dp[i - 1][j]
dp[i][(j + num) % 10] += dp[i - 1][j]
# 统计符合条件的方式
count = 0
# 两组都非空
count += (dp[n][A]+dp[n][B])
# print(dp)
# 允许一组为空的情况
total_digit = total_sum % 10
if total_digit != (A+B)%10:
count = 0
if total_digit == A or total_digit ==B:
count += 1;
return count
if __name__ == "__main__":
# You can add more test cases here
print(solution(3, 1, 2, [1,1,1]) == 3 )
print(solution(3, 3, 5, [1,1,1]) == 1 )
print(solution(2, 1, 1, [1,1]) == 2 )