Partition Array for Maximum Sum 分隔数组以得到最大和
问题描述:
给你一个整数数组 arr
,请你将该数组分隔为长度 最多 为 k 的一些(连续)子数组。分隔完成后,每个子数组的中的所有值都会变为该子数组中的最大值。
arr.length 范围[1,500],arr[i]范围[0,10^9] ,k范围[1,arr.length]
分析
就是一个数组arr,对其计算和,可以将原始数组进行分割,分割成为若干的子数组,子数组的长度 l e n g t h < = k length<=k length<=k,分割后的子数组的值都会变成该子数组中的最大值。
也就是说,应该存在一种分割,使得每个子数组都得到最大值,从而使得整个数组取的最大。
但是这样的分割是不确定的,长度可以是
1
1
1,最大是
k
−
1
k-1
k−1,所以只能尝试传统的递归搜索,
d
f
s
(
i
)
表示以下标
i
为结尾时可以得到的最大和
dfs(i) 表示以下标i为结尾时可以得到的最大和
dfs(i)表示以下标i为结尾时可以得到的最大和。
D
F
S
(
i
)
=
max
j
=
max
(
i
−
k
,
0
)
i
−
1
{
D
F
S
(
j
)
+
(
i
−
j
)
×
max
p
=
j
+
1
i
a
r
r
[
p
]
}
DFS(i)= \max_{j=\max(i-k,0)}^{i-1} \{DFS(j)+ (i-j) \times \max_{p=j+1}^{i}arr[p] \}
DFS(i)=j=max(i−k,0)maxi−1{DFS(j)+(i−j)×p=j+1maxiarr[p]}
由于 d f s dfs dfs的定义,所以可以将 d f s ( 0 ) → d f s ( k ) dfs(0)\rightarrow dfs(k) dfs(0)→dfs(k)的结果预处理,并作为边界。
那么最终的结果,就是 d f s ( n − 1 ) dfs(n-1) dfs(n−1).
而整个dfs的时间复杂度是基于 n n n的规模,时间复杂度很大,而中间又会出现非常多的重复,所以memo记忆化就可以上了。
既然可以通过 d f s + m e m o dfs+memo dfs+memo递归回溯得到结果,那么正向的递推也可以。
f [ i ] 表示下标 i 结尾的数组划分子数组后,可以得到的最大和 f[i]表示下标i结尾的数组划分子数组后,可以得到的最大和 f[i]表示下标i结尾的数组划分子数组后,可以得到的最大和。
状态转移方程
f
[
i
]
=
max
j
=
max
(
i
−
k
,
0
)
i
−
1
{
f
(
j
)
+
(
i
−
j
)
×
m
x
}
m
x
表示
a
r
r
[
j
+
1
]
→
a
r
r
[
i
]
的最大值。
f[i] = \max_{j=\max(i-k,0)}^{i-1} \{f(j)+ (i-j) \times mx \}\\ mx表示arr[j+1]\rightarrow arr[i]的最大值。
f[i]=j=max(i−k,0)maxi−1{f(j)+(i−j)×mx}mx表示arr[j+1]→arr[i]的最大值。
$$
丐版方程:f[i] =max( f[j] + (i-j)*mx ) j<=i-1&&j>=i-k
代码
public int maxSumAfterPartitioning(int[] arr, int k) {
int n = arr.length;
int[] d = new int[n + 1];
for (int i = 1; i <= n; i++) {
int maxValue = arr[i - 1];
for (int j = i - 1; j >= 0 && j >= i - k; j--) {
d[i] = Math.max(d[i], d[j] + maxValue * (i - j));
if (j > 0) {
maxValue = Math.max(maxValue, arr[j - 1]);
}
}
}
return d[n];
}
时间复杂度 O ( N k ) O(Nk) O(Nk)
空间复杂度: O ( N ) O(N) O(N)
Tag
Array
Dynamic Programming