📌  相关文章
📜  最小化将数组划分为 K 个组的成本(1)

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

最小化将数组划分为 K 个组的成本

将一个数组划分为 K 个组,可以看作是将数组分割为 K 段。而每段的成本取决于该段中最大的数值。

例如,对于数组 [5, 2, 4, 7, 1, 3, 6, 8],将其划分为 4 个组的最小成本为 7,具体划分方法为:[5, 2], [4, 7], [1, 3], [6, 8]。

本题需要求解将一个数组划分为 K 个组的最小成本,算法的时间复杂度需在 O(nklog(n)) 级别。

解题思路

可使用动态规划求解。令 dp(i, k) 表示将数组前 i 个元素划分为 k 个组的最小成本。

假设将数组从最后一个元素开始不断往前分割,于是可将 dp(i, k) 表示为下面的形式:

dp(i, k) = min(
              max(arr[j+1:i+1]),  // 最后一组的成本
              dp(j, k-1)         // 前面 k-1 个组的最小成本
            )
其中 j 表示最后一个组的起始下标,满足 k-1 <= j <= i-1。

最后一组的成本很容易计算,问题是前面 k-1 个组的最小成本。

至此,问题转化为了以前 i 个元素划分为 k-1 个组的最小成本,可以用 dp(i, k) 来表示。

状态转移方程如下:

dp(i, k) = min(
              max(arr[j+1:i+1]),  // 最后一组的成本
              dp(j, k-1)         // 前面 k-1 个组的最小成本
            )
其中 j 表示最后一个组的起始下标,满足 k-2 <= j <= i-1。

初始状态:dp(0, 0) = 0dp(i, 1) = max(arr[0:i+1])

最终结果:dp(n-1, k)

代码演示

为了方便理解,下面的程序代码未进行优化。

def splitArray(nums: List[int], m: int) -> int:
    n, inf = len(nums), 1e18
    s, f = [[inf] * (m+1) for i in range(n)], [[-1] * (m+1) for i in range(n)]
    pre = [0] * n
    for i in range(n):
        pre[i] = pre[i-1] + nums[i] if i != 0 else nums[i]
        s[i][1], f[i][1] = pre[i], -1
        
    for k in range(2, m+1):
        for i in range(k-1, n):
            s[i][k] = inf
            for j in range(k-2, i):
                tmp = max(s[j][k-1], pre[i]-pre[j])
                if tmp < s[i][k]:
                    s[i][k], f[i][k] = tmp, j
    return s[n-1][m]