📅  最后修改于: 2023-12-03 15:25:19.620000             🧑  作者: Mango
有一个数组,要求将其拆分为K个子数组,使得每个子数组中的相邻元素之间的绝对差的总和最小。
先定义一个二维数组dp,dp[i][j] 表示将前 i 个元素分成 j 个子数组时的最小花费。
状态转移方程如下:
dp[i][j] = min(dp[i][j], dp[k][j - 1] + cost[k+1][i])
其中,k是数组中第一个子数组的右端点,cost[k+1][i] 表示将数字 k+1 到 i 区间的元素分成一个子数组的花费。
最终结果为dp[n][k],其中n为数组长度。
题目中的答案是一个整数,相邻元素之间的绝对差的总和最小。因此我们可以使用二分答案的方法对答案进行搜索。
假设一个mid为当前的二分答案,我们可以遍历整个数组,将每个数加入子数组中,直到子数组中相邻元素的差值之和超过了mid,此时就将当前子数组的数加入下一个子数组中。
最终如果数量小于k,则说明当前答案偏大,需要向左侧区间搜索,否则需要向右侧区间搜索,直到搜索完整个区间,最终的答案就是左侧最后一个符合条件的数。
def splitArray(nums, k):
n = len(nums)
dp = [[float('inf')] * (k+1) for _ in range(n+1)]
dp[0][0] = 0
pre_sum = [0]*(n+1)
for i in range(1, n+1):
pre_sum[i] = pre_sum[i-1] + nums[i-1]
for i in range(1, n+1):
for j in range(1, k+1):
for p in range(j-1, i):
dp[i][j] = min(dp[i][j], dp[p][j-1] + pre_sum[i] - pre_sum[p])
return dp[n][k]
def check(mid, nums, k):
cnt, cur_sum = 1, 0
for i in range(len(nums)):
if cur_sum + nums[i] > mid:
cnt += 1
cur_sum = nums[i]
else:
cur_sum += nums[i]
return cnt <= k
def splitArray(nums, k):
left, right = max(nums), sum(nums)
while left < right:
mid = (left + right) // 2
if check(mid, nums, k):
right = mid
else:
left = mid + 1
return left