题目描述
给出一些不同颜色的盒子,盒子的颜色由数字表示,即不同的数字表示不同的颜色。你将经过若干轮操作去去掉盒子,直到所有的盒子都去掉为止。每一轮你可以移除具有相同颜色的连续 k 个盒子(k >= 1),这样一轮之后你将得到 k*k 个积分。
当你将所有盒子都去掉之后,求你能获得的最大积分和。
题目分析
1.初步分析
这道题难度困难,看上去还是挺难的,有点像数学问题,我们可以简单的分析一下这个模型:
首先,当给我们一串数字让我们去运算之后,为了分数越高,肯定应该尽量往更多重复的数字上面靠拢。
这里有个简单的理论推导:
- a2 + b2 < (a+b)2
所以我第一个想法是:
- 中间有重复的数字首先移除
- 从头尾开始寻找单个的数字,将他们中间的数字消除,实现数字变多后消除
但是很快我又发现了问题,如果出现了:
[2,2,2,3,3,3,2,2,3]
这种形式,我们又该如何计算呢?
2.进一步分析
上面那个例子其实是把5个的一起消除分数最高,所以我假设这种多个数字重复,相互嵌套的情况,一律按照能够连起来数字越多优先级越高的方式消除
然后我们再假设:
[2,2,3,3,5,5,2,5,5,3,3,2,2]
发现这种情况就不适用了。此时我觉得人有点晕,又好好的看了一眼题目,发现它只需要最终的数字,那么我们是不是就应该使用穷举的方法得到最高的那个数字呢?
经过一段时间的思考之后我发现,其实这个问题有点像动态规划的那种方式。
再经过一段时间思考我选择看一下官方的题解…
题目解答
先把答案代码放上来:
class Solution {
public int removeBoxes(int[] boxes) {
int[][][] dp = new int[100][100][100];
return calculatePoints(boxes, dp, 0, boxes.length - 1, 0);
}
public int calculatePoints(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];
while (r > l && boxes[r] == boxes[r - 1]) {
r--;
k++;
}
dp[l][r][k] = calculatePoints(boxes, dp, l, r - 1, 0) + (k + 1) * (k + 1);
for (int i = l; i < r; i++) {
if (boxes[i] == boxes[r]) {
dp[l][r][k] = Math.max(dp[l][r][k], calculatePoints(boxes, dp, l, i, k + 1) + calculatePoints(boxes, dp, i + 1, r - 1, 0));
}
}
return dp[l][r][k];
}
}
代码分析:
我研究了一下官方答案,大概是这么一个意思,这个方法是通过动态规划加递归来解决的。
-
程序开始执行
-
程序继续执行
-
递归到底
递归到底之后,获得了每个情况的值,随着不停回代,最终获得最大的数。
代码进一步分析
一开始看递归还是挺难理解的,后来想了一下是这样的,第一个递归的过程类似于直接消除看上去就连着的数字,后面那个for循环,相当于讨论是直接消除连着的数字大还是把中间数字消除后攒了更多相同数字组合后更大。
写后感
让我自己写,我应该是写不出来的,听说这是去年秋招某公司的面试题,我觉得如果面试的时候遇到这种题应该放松心情,反正也写不出来,不要太过焦虑。