题目描述
小F需要将一个数组分为两组,使得:
- 一组数字的和的个位数等于给定值 ;
- 另一组数字的和的个位数等于给定值 。
允许一种特殊情况:
- 其中一组为空,但剩余数字的和的个位数必须等于 或 。
小F需要计算所有满足要求的分组方式。
示例:
-
输入:
n = 3, A = 1, B = 2, array_a = [1, 1, 1]输出:3
-
输入:
n = 3, A = 3, B = 5, array_a = [1, 1, 1]输出:1
解题思路
本题需要用到动态规划和位运算的结合来解决。
1. 问题建模
- 我们可以枚举每个数字是否属于某一组,通过记录两组和的状态来判断是否满足条件。
- 用动态规划来存储和状态,记录可能的划分方式。
关键点
- 所有数字总和的个位数决定了是否可以分成两组满足条件;否则直接返回 0。
- 动态规划的核心是逐步更新可能的和状态。
2. 动态规划状态设计
状态定义
- 使用一个二维数组
dp[i][x]表示前 个数字中可以组成和的个位数为 的分组方式数。
状态转移
- 遍历数组中的每个数字,将其加入到可能的当前和中:
- 假设当前数字为 ,尝试加入到各个位数状态 中:
- 新的状态为
- 假设当前数字为 ,尝试加入到各个位数状态 中:
初始条件
- ,表示还未加入任何数字时,和为 0 的状态。
3. 特殊情况处理
- 如果所有数字总和的个位数不等于 或 ,则直接输出 0。
- 允许某一组为空的条件下,总和的个位数等于 或 时,直接考虑特殊情况。
4. 优化
- 使用滚动数组优化空间复杂度,将状态压缩为一维数组。
代码详解
以下是代码实现及注释:
# 定义求解函数
def count_partitions(n, A, B, array_a):
# 计算总和的个位数
total_sum = sum(array_a) % 10
# 特殊情况:总和的个位数不能满足 A 或 B
if total_sum != A and total_sum != B:
return 0
# 动态规划初始化
dp = [0] * 10
dp[0] = 1 # 初始状态
# 遍历数组,更新状态
for num in array_a:
next_dp = dp[:]
for x in range(10):
next_dp[(x + num) % 10] += dp[x]
dp = next_dp
# 计算结果
result = dp[A] + dp[B]
return result
# 测试样例
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
print(count_partitions(5, 3, 7, [2, 3, 5, 7, 9])) # 输出:0
知识总结
动态规划总结
- 状态设计:将问题分解为子问题,定义状态存储计算结果。
- 状态转移方程:清晰描述如何从上一个状态推导到当前状态。
- 边界条件:考虑初始状态和特殊情况。
数学知识
- 模运算 的性质在问题中多次出现,理解其在动态规划中的作用。
- 总和的个位数是关键分析点,通过数学计算可以简化问题。
学习计划
1. 制定刷题计划
- 每天练习 3-5 道动态规划相关题目,逐步提升思维能力。
- 将题目分为初级(如简单路径问题)、中级(如背包问题)、高级(如本题)。
2. 利用错题进行针对性学习
- 每次错题后重新分析,归纳出错误的原因(如状态定义或转移错误)。
- 使用豆包MarsCode AI的题目推荐功能,练习相似题目。
3. 提升方法
- 学会画出状态转移图,将抽象问题具体化。
- 对复杂问题分步骤拆解,降低理解难度。
学习建议
- 对于入门同学:从简单题目入手,逐步理解动态规划思想。
- 每天记录刷题心得,形成自己的知识库。通过多练习和反思,让思维更加敏捷,逐步攻克更复杂的题目。