📌  相关文章
📜  将数组拆分为K个子数组,相邻元素之间的绝对差的总和最小(1)

📅  最后修改于: 2023-12-03 15:25:19.620000             🧑  作者: Mango

将数组拆分为K个子数组,相邻元素之间的绝对差的总和最小

介绍

有一个数组,要求将其拆分为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
参考文献