题目地址:
https://leetcode.com/problems/remove-boxes/
给定一个长 n n n数组 A A A,每次可以将连续的一段相等的数删去,得分是这一段数的个数的平方。问将其数全部删去得分最大是多少。
思路是动态规划。设
f
[
i
]
[
j
]
[
k
]
f[i][j][k]
f[i][j][k]是要清空
A
[
i
:
j
]
A[i:j]
A[i:j]这个区间,最后是删除了
k
k
k个数,并且使得
A
[
i
]
A[i]
A[i]是最后一个被删除的数的情况下,得到的最大分数。首先对于任意方案,如果
A
[
i
]
A[i]
A[i]不是最后删除的,那么我们可以将删除
A
[
i
]
A[i]
A[i]的那一步放到最后去做,这样并不影响得分。所以
f
[
i
]
[
j
]
[
k
]
f[i][j][k]
f[i][j][k]可以枚举到所有情况。设
g
[
i
]
[
j
]
g[i][j]
g[i][j]是清空
A
[
i
:
j
]
A[i:j]
A[i:j]的所有方案的最大得分,即
g
[
i
]
[
j
]
=
max
1
≤
k
≤
j
−
i
+
1
f
[
i
]
[
j
]
[
k
]
g[i][j]=\max_{1\le k\le j - i + 1}f[i][j][k]
g[i][j]=max1≤k≤j−i+1f[i][j][k]。对于
f
[
i
]
[
j
]
[
k
]
f[i][j][k]
f[i][j][k],我们枚举
A
[
i
]
A[i]
A[i]是单独删除的,还是和别的数一起删的。
1、如果其是单独删的,那么删掉它得分是
1
1
1,其右边的数删除的最大得分是
g
[
i
+
1
]
[
j
]
g[i+1][j]
g[i+1][j],即
f
[
i
]
[
j
]
[
1
]
=
1
+
g
[
i
+
1
]
[
j
]
f[i][j][1]=1+g[i+1][j]
f[i][j][1]=1+g[i+1][j];
2、如果其不是单独删的,我们枚举最后一次删除的时候,除了
A
[
i
]
A[i]
A[i]以外最左边被删除的数,设其是
A
[
u
]
A[u]
A[u],那么
u
=
i
+
1
,
i
+
2
,
.
.
.
,
j
u=i+1,i+2,...,j
u=i+1,i+2,...,j,并且要保证
A
[
u
]
=
A
[
i
]
A[u]=A[i]
A[u]=A[i](颜色相同才能一起删)。这样将区间
[
i
:
j
]
[i:j]
[i:j]的删除过程分为了两步,即删除
A
[
i
+
1
:
u
−
1
]
A[i+1:u-1]
A[i+1:u−1]和删除
A
[
i
]
,
A
[
u
:
j
]
A[i],A[u:j]
A[i],A[u:j](这里可以看成
A
[
i
]
A[i]
A[i]和
A
[
u
:
j
]
A[u:j]
A[u:j]这
j
−
u
j-u
j−u个数拼成了一个新区间去删除。之所以可以这么做,是因为对于任意方案,我们总可以重排,使得
A
[
i
+
1
:
u
−
1
]
A[i+1:u-1]
A[i+1:u−1]先删,这样其余的删除操作就可以看成是在
[
A
[
i
]
,
A
[
u
]
,
.
.
.
,
A
[
j
]
]
[A[i],A[u],...,A[j]]
[A[i],A[u],...,A[j]]这样的新数组上操作了),第一部分的最大得分是
g
[
i
+
1
:
u
−
1
]
g[i+1:u-1]
g[i+1:u−1],而第二部分的最大得分,由于最后一步是左边连续删除了
k
k
k个,所以是
A
[
u
:
j
]
A[u:j]
A[u:j]是左边连续删除了
k
−
1
k-1
k−1个,而对于
A
[
u
:
j
]
A[u:j]
A[u:j]是左边连续删除了
k
−
1
k-1
k−1个这个的最大得分是
f
[
u
]
[
j
]
[
k
−
1
]
f[u][j][k-1]
f[u][j][k−1],现在我们是最后删
k
k
k个,所以得分应该是
f
[
u
]
[
j
]
[
k
−
1
]
−
(
k
−
1
)
2
+
k
2
f[u][j][k-1]-(k-1)^2+k^2
f[u][j][k−1]−(k−1)2+k2。所以综上:
f
[
i
]
[
j
]
[
k
]
=
{
1
+
g
[
i
+
1
]
[
j
]
,
k
=
1
max
u
>
i
∧
A
[
u
]
=
A
[
i
]
{
g
[
i
+
1
]
[
u
−
1
]
+
f
[
u
]
[
j
]
[
k
−
1
]
−
(
k
−
1
)
2
+
k
2
}
,
k
>
1
f[i][j][k]=\begin{cases}1+g[i+1][j], k=1\\\max_{u>i\land A[u]=A[i]}\{g[i+1][u-1]+ f[u][j][k-1]-(k-1)^2+k^2 \} ,k>1 \end{cases}
f[i][j][k]={1+g[i+1][j],k=1maxu>i∧A[u]=A[i]{g[i+1][u−1]+f[u][j][k−1]−(k−1)2+k2},k>1当然这里,如果
g
g
g那里的区间长度小于
0
0
0,则
g
g
g取
0
0
0。代码如下:
import java.util.Arrays;
public class Solution {
public int removeBoxes(int[] A) {
int n = A.length;
// f[i][j][k]是清空A[i : j]并且最后一步是删除了A[i]且恰好删了k个数的所有方案里最大的得分
int[][][] f = new int[n][n][n + 1];
// g[i][j]是清空A[i : j]的最大得分
int[][] g = new int[n][n];
// 先初始化为最小值
for (int i = 0; i < n; i++) {
for (int[] row : f[i]) {
Arrays.fill(row, -1 << 30);
}
Arrays.fill(g[i], -1 << 30);
}
// 枚举区间长度为1的情况
for (int i = 0; i < n; i++) {
f[i][i][1] = g[i][i] = 1;
}
// 枚举区间长度大于1的情况,先枚举区间长度
for (int len = 2; len <= n; len++) {
// 再枚举左端点
for (int i = 0; i + len - 1 < n; i++) {
// 算出右端点
int j = i + len - 1;
// 枚举k = 1的情况
f[i][j][1] = 1 + g[i + 1][j];
// 更新g[i][j]
g[i][j] = Math.max(g[i][j], f[i][j][1]);
// 枚举k大于1的情况
for (int k = 2; k <= len; k++) {
// 枚举最后一次删除的时候,删除的所有数里左起第二个数是谁;
// 由于最后删除的数的个数是k个,所以j - u + 2 >= k
for (int u = i + 1; u <= j - k + 2; u++) {
// 这个第二个数一定要与A[i]相等
if (A[u] != A[i]) {
continue;
}
// 预存一下g的部分
int t = i + 1 <= u - 1 ? g[i + 1][u - 1] : 0;
// 更新f
f[i][j][k] = Math.max(f[i][j][k], t + f[u][j][k - 1] - (k - 1) * (k - 1) + k * k);
}
// 更新g
g[i][j] = Math.max(g[i][j], f[i][j][k]);
}
}
}
return g[0][n - 1];
}
}
时间复杂度 O ( n 4 ) O(n^4) O(n4),空间 O ( n 3 ) O(n^3) O(n3)。