区间动态规划例题:最小划分问题(平衡划分问题)
字数 1134 2025-11-03 08:34:44
区间动态规划例题:最小划分问题(平衡划分问题)
题目描述
给定一个正整数数组 nums,判断是否可以将该数组划分成两个子集,使得两个子集的元素和相等。如果可能,返回 true,否则返回 false。例如,对于数组 [1, 5, 11, 5],可以划分成 [1, 5, 5] 和 [11],两个子集的和均为 11,因此返回 true。
解题思路
- 问题转化:若数组总和为奇数,直接返回
false(因为无法平分)。若总和为偶数,则目标子集和target = sum(nums) / 2。问题转化为:是否存在子集的和为target?这本质上是一个 0-1 背包问题。 - 区间动态规划视角:虽然此题常用一维DP解决,但我们可以从区间DP的角度理解:定义
dp[i][j]表示前i个元素是否能组成和j。但更符合区间DP思想的解法是直接处理子数组的和关系。 - 状态定义:设
dp[i][j]表示从数组下标0到i的元素中,能否选出若干个数使其和为j。 - 状态转移:
- 若
j >= nums[i]:dp[i][j] = dp[i-1][j] || dp[i-1][j-nums[i]](不选当前元素或选当前元素)。 - 否则:
dp[i][j] = dp[i-1][j]。
- 若
- 初始化:
dp[0][0] = true,dp[0][nums[0]] = true(若nums[0] <= target)。 - 结果:检查
dp[n-1][target]是否为true。
逐步详解
- 计算总和:
total = sum(nums) if total % 2 != 0: return False target = total // 2 - 初始化DP表:
创建二维数组dp,维度为n × (target+1),初始化为False。dp[0][0] = True if nums[0] <= target: dp[0][nums[0]] = True - 填充DP表:
遍历每个元素i(从 1 到 n-1),对于每个和j(从 0 到 target):if j >= nums[i]: dp[i][j] = dp[i-1][j] or dp[i-1][j-nums[i]] else: dp[i][j] = dp[i-1][j] - 优化空间:
实际可压缩为一维DP,倒序遍历j避免覆盖:dp = [False] * (target+1) dp[0] = True for num in nums: for j in range(target, num-1, -1): dp[j] = dp[j] or dp[j-num] - 返回结果:
return dp[target]
实例验证
以 nums = [1, 5, 11, 5] 为例:
- 总和 = 22,target = 11。
- 初始化
dp[0]=True。 - 处理
num=1:dp[1]=True。 - 处理
num=5:dp[6]=True(由dp[1]推导)。 - 处理
num=11:dp[11]=True(由dp[0]推导)。 - 最终
dp[11]为True,返回true。
关键点
- 此题的区间DP思想体现在对子数组和的逐步构建。
- 空间优化后时间复杂度为 O(n×target),适用于 target 不大的场景。