给出一些不同颜色的盒子,盒子的颜色由数字表示,即不同的数字表示不同的颜色。你将经过若干轮操作去去掉盒子,直到所有的盒子都去掉为止。每一轮你可以移除具有相同颜色的连续 k 个盒子(k >= 1),这样一轮之后你将得到 k*k 个积分。当你将所有盒子都去掉之后,求你能获得的最大积分和。
例如:
输入:boxes = [1,3,2,2,2,3,4,3,1]
输出:23
解释:
[1, 3, 2, 2, 2, 3, 4, 3, 1]
----> [1, 3, 3, 4, 3, 1] (3*3=9 分)
----> [1, 3, 3, 3, 1] (1*1=1 分)
----> [1, 1] (3*3=9 分)
----> [] (2*2=4 分) // 来源:力扣(LeetCode)/546
解决思路:
- 以官方解答例子为例,例如[6,3,6,5,6,7,6,6,8,6],盒子从最右侧看,第一种方法就是消除从最右侧开始,也就是从红色的6开始,消除和它相连的同类型盒子,第一轮后你会得到[6,3,6,5,6,7,6,6,8],第二轮后得到 [6,3,6,5,6,7,6,6],以此类推。
- 第二种方法还是从最右侧开始,,要消除阻碍它和同颜色盒子相连的盒子,也就是为了要让6盒子相连,需要除掉8盒子。
最后方法一和方法二对比,最大的就是题目所求的。
class Solution {
public int removeBoxes(int[] boxes) {
// 用于存储之前计算过的状态,避免重复计算
int[][][] dp = new int[100][100][100];
return cal(boxes, dp, 0, boxes.length - 1, 0);
}
public int cal(int[] boxes, int[][][] dp, int l, int r, int k) {
if (l > r) {
return 0;
}
if (dp[l][r][k] != 0) {
return dp[l][r][k];
}
// 计算右边有几个跟最右边一个(boxes[r])相等, 如果相等则把右边界左移到不相同的元素之后一个为止,移动过程中同步改动k
while (r > l && boxes[r] == boxes[r-1]) {
r--;
k++;
}
// 计算把右边k+1个消除时的得分
dp[l][r][k] = cal(boxes, dp, l, r-1, 0) + (k+1)*(k+1);
// 从右边界开始向左寻找跟外部k个元素相等的元素,如果相等则剔除掉这些不相等的,让后面一段连起来。
// 此时得分就是中间消除中间一段不连续部分的得分和剩下来部分的得分
// 比较这个得分和原来计算过其他方案的得分,去最大值覆盖到状态数组dp中
for (int i = r-1; i >= l; --i) {
if (boxes[i] == boxes[r]) {//第一个cal计算剩下左面盒子的值,第二个计算已被除掉盒子的值
dp[l][r][k] = Math.max(dp[l][r][k], cal(boxes, dp, l, i, k+1) + cal(boxes, dp, i+1, r-1, 0));}
}
return dp[l][r][k];
}
}