📅  最后修改于: 2023-12-03 15:10:36.486000             🧑  作者: Mango
将一个数组划分为 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) = 0
,dp(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]