带限制的区间合并最小操作次数问题
题目描述
给定一个长度为 \(n\) 的数组 nums 和一个整数 limit,你可以进行如下操作:
- 选择两个相邻的区间(每个区间是原数组的一个连续子数组,且这两个区间在原数组中也是相邻的,即第一个区间的右端点+1等于第二个区间的左端点),
- 如果这两个区间中所有元素的和都小于等于
limit,则可以将它们合并成一个新区间(合并后新区间的元素和是两个区间和的和)。
每次操作合并后,原数组的区间数减少 1。
问:最少需要多少次操作,可以将整个数组合并成一个区间?如果无法合并成一个区间,返回 -1。
示例
nums = [2, 3, 1, 5, 4]
limit = 6
初始区间划分:可以视为每个元素单独成一个区间:[2], [3], [1], [5], [4]。
合并只能在相邻区间和 ≤ limit 时进行。
一种最少操作的方式(需 4 次合并,因为 5 个区间合并成 1 个区间需要 4 次操作):
- 合并
[2]和[3](和=5 ≤6)→[5], [1], [5], [4] - 合并
[5]和[1](和=6 ≤6)→[6], [5], [4] - 合并
[6]和[5](和=11>6,不行),所以先合并[5]和[4](和=9>6,不行)——这里需要尝试别的顺序。
实际上,检查发现:初始时相邻区间和都 ≤6 吗?
[2]+[3]=5 ≤6 ✅
[3]+[1]=4 ≤6 ✅
[1]+[5]=6 ≤6 ✅
[5]+[4]=9>6 ❌
所以 [5] 和 [4] 不能直接合并。
但我们可以先合并前面的,使 5 和 4 不再相邻,从而避免无法合并到最后。
最终最少操作次数是 4 吗?测试发现:
- 先合并 [2,3]→5
- 合并 [1,5]→6(因为此时区间是 [5],[1],[5],[4] → 合并 [1] 和 [5] 得到和=6)得到 [5],[6],[4]
- 此时 [5]+[6]=11>6 不能合并,[6]+[4]=10>6 不能合并,陷入死局,无法合并成一个区间。
所以返回 -1。
但题目要求是:在必须合并到只剩一个区间的情况下,是否可能?
如果不可能,返回 -1。
问题分析
这是一个区间动态规划问题,因为:
- 涉及对数组的连续子数组(区间)进行操作。
- 操作有顺序,不同的合并顺序会影响后续能否继续合并。
- 我们需要判断能否合并到只剩一个区间,并求最小操作次数(操作次数 = 初始区间数 n - 最终区间数 1 = n-1,但前提是每次操作合法)。
实际上,我们不在乎操作次数,只在乎能否合并成一个区间。能合并的话,操作次数固定为 n-1,但必须存在一种合并顺序使得每一步合并的两区间和 ≤ limit。
所以问题变成:判断是否存在一种合并顺序,使得从初始 n 个单元素区间出发,每次合并两个相邻且和 ≤ limit 的区间,最终合并成一个区间。
等价于:在数组上不断合并相邻的段,每段的和不超过 limit 时才可与相邻段合并,问能否合并成一段。
动态规划思路
定义 dp[i][j] = 是否能将子数组 nums[i..j] 合并成一个区间(这个区间的和必须 ≤ limit 吗?不一定,因为最后整个数组的和可能大于 limit,但中间合并时要满足每次合并的两个子区间的各自和都 ≤ limit)。
注意:合并的条件是两个相邻区间的和分别 ≤ limit。
如果我们把 nums[i..j] 合并成了一个区间,那在合并的某个最后一步,一定是将 [i..k] 和 [k+1..j] 合并,且:
[i..k]能合并成一个区间(其和记为 sum(i,k))[k+1..j]能合并成一个区间(其和记为 sum(k+1,j))- 在合并它们之前,
[i..k]已经是一个区间,[k+1..j]已经是一个区间,它们的和都 ≤ limit,才能合并。
所以条件:
sum(i,k) ≤ limitsum(k+1,j) ≤ limit
且 dp[i][k] = true 且 dp[k+1][j] = true。
另外,如果 i=j,则单个元素区间必然满足和 ≤ limit(只要 nums[i] ≤ limit),否则这个元素自己就 > limit,永远不能和任何区间合并,那么包含它的任何区间都不能合并成一个区间。
预处理:
- 前缀和数组
prefix用于 O(1) 求区间和。 - 先检查每个元素是否 ≤ limit,如果某个元素 > limit,则包含它的任何区间都不能合并成一个区间(因为该元素单独成段时和>limit,不能参与合并)。
不过,如果这个元素 > limit,但和别的元素加起来平均值小,但定义是合并前的两个区间各自和 ≤ limit,所以即使元素本身>limit,它自己一个区间就不能和别的合并,所以包含它的任何区间不可能合并成一个区间。
所以先判断:若存在 nums[i] > limit,直接返回 -1。
DP 状态定义与转移
dp[i][j] = 是否能将 nums[i..j] 合并成一个区间(true/false)。
初始化:
dp[i][i] = true 当且仅当 nums[i] ≤ limit,否则 false。
转移:
枚举分界点 k 从 i 到 j-1:
如果 dp[i][k] && dp[k+1][j] && sum(i,k) ≤ limit && sum(k+1,j) ≤ limit,则 dp[i][j] = true。
最终答案:
如果 dp[0][n-1] = true,则最少操作次数固定为 n-1,否则返回 -1。
逐步计算示例
例:nums = [2, 3, 1, 5, 4], limit=6`
先检查每个元素:都 ≤6 ✅
前缀和:prefix = [0,2,5,6,11,15]
n=5
初始化 dp[i][i]=true
区间长度 len=2:
- [0,1]: sum(0,0)=2≤6, sum(1,1)=3≤6, dp[0][0]=true, dp[1][1]=true → dp[0][1]=true
- [1,2]: sum(1,1)=3≤6, sum(2,2)=1≤6 → dp[1][2]=true
- [2,3]: sum(2,2)=1≤6, sum(3,3)=5≤6 → dp[2][3]=true
- [3,4]: sum(3,3)=5≤6, sum(4,4)=4≤6 → dp[3][4]=true
len=3:
- [0,2]: 分界 k=1:
[0,1] 和 [2,2]: sum(0,1)=5≤6✅, sum(2,2)=1≤6✅, dp[0][1]=true, dp[2][2]=true → dp[0][2]=true - [1,3]: 分界 k=2:
[1,2] 和 [3,3]: sum(1,2)=4≤6✅, sum(3,3)=5≤6✅, dp[1][2]=true, dp[3][3]=true → dp[1][3]=true - [2,4]: 分界 k=3:
[2,3] 和 [4,4]: sum(2,3)=6≤6✅, sum(4,4)=4≤6✅, dp[2][3]=true, dp[4][4]=true → dp[2][4]=true
len=4:
- [0,3]: 分界 k=2:
[0,2] 和 [3,3]: sum(0,2)=6≤6✅, sum(3,3)=5≤6✅, dp[0][2]=true, dp[3][3]=true → dp[0][3]=true - [1,4]: 分界 k=2:
[1,2] 和 [3,4]: sum(1,2)=4≤6✅, sum(3,4)=9>6❌ → 不行
k=3: [1,3] 和 [4,4]: sum(1,3)=9>6❌ → 不行 → dp[1][4]=false
len=5: [0,4]
分界 k=3: [0,3] 和 [4,4]: sum(0,3)=11>6❌
k=2: [0,2] 和 [3,4]: sum(0,2)=6≤6✅, sum(3,4)=9>6❌
k=1: [0,1] 和 [2,4]: sum(0,1)=5≤6✅, sum(2,4)=10>6❌
→ 所有分界都至少有一个区间和>6,所以 dp[0][4]=false
最终 dp[0][4]=false,无法合并成一个区间,返回 -1。
算法复杂度
- 时间复杂度:O(n³),三重循环(len, i, k)。
- 空间复杂度:O(n²)。
代码框架(Python)
def min_operations_to_merge(nums, limit):
n = len(nums)
for x in nums:
if x > limit:
return -1
prefix = [0] * (n + 1)
for i in range(n):
prefix[i+1] = prefix[i] + nums[i]
def sum_range(l, r):
return prefix[r+1] - prefix[l]
dp = [[False] * n for _ in range(n)]
for i in range(n):
dp[i][i] = (nums[i] <= limit)
for length in range(2, n+1):
for i in range(n-length+1):
j = i + length - 1
for k in range(i, j):
if dp[i][k] and dp[k+1][j] and sum_range(i, k) <= limit and sum_range(k+1, j) <= limit:
dp[i][j] = True
break
return n-1 if dp[0][n-1] else -1
总结
这道题是区间 DP 的可行性判断问题,关键点在于:
- 合并条件是两个相邻区间各自的和 ≤ limit,而不是它们合并后的和 ≤ limit。
- 如果存在单个元素 > limit,则不可能完成合并。
- 状态转移时,必须枚举划分点,检查左右两部分是否能分别合并成一段,且这两段在合并前的和满足限制。