Given several boxes with different colors represented by different positive numbers. 
You may experience several rounds to remove boxes until there is no box left. Each time you can choose some continuous boxes with the same color (composed of k boxes, k >= 1), remove them and get k*k points.
Find the maximum points you can get.

Example 1:
Input:

[1, 3, 2, 2, 2, 3, 4, 3, 1]

Output:

23

Explanation:

[1, 3, 2, 2, 2, 3, 4, 3, 1]
----> [1, 3, 3, 4, 3, 1] (3*3=9 points)
----> [1, 3, 3, 3, 1] (1*1=1 points)
----> [1, 1] (3*3=9 points)
----> [] (2*2=4 points)

Note: The number of boxes n would not exceed 100.

刚开始看这道题的时候,感觉跟之前那道Zuma Game很像,于是就写了一个暴力破解的方法,结果TLE了。无奈之下只好上网搜大神们的解法,又看了fun4LeetCode大神写的帖子,之前那道Reverse Pairs就是参考的fun4LeetCode大神的帖子,惊为天人,这次又是这般精彩,大神请收下我的膝盖。那么下面的解法就大部分参考fun4LeetCode大神的帖子来讲解吧。在之前帖子Reverse Pairs的讲解中,大神归纳了两种重现模式,我们这里也试着看能不能套用上。对于这种看来看去都没思路的题来说,抽象建模的能力就非常的重要了。对于题目中的具体场景啊,具体代表的东西我们都可忽略不看,这样能帮助我们接近问题的本质,这道题的本质就是一个数组,我们每次消去一个或多个数字,并获得相应的分数,让我们求最高能获得的分数。而之前那道Zuma Game也是给了一个数组,让我们往某个位置加数字,使得相同的数字至少有3个才能消除,二者是不是很像呢,但是其实解法却差别很大。那道题之所以暴力破解没有问题是因为数组的长度和给定的数字个数都有限制,而且都是相对较小的数,那么即便遍历所有情况也不会有太大的计算量。而这道题就不一样了,既然不能暴力破解,那么对于这种玩数组和子数组的题,刷题老司机们都会优先考虑用DP来做吧。既然要玩子数组,肯定要限定子数组的范围,那么至少应该是个二维的dp数组,其中dp[i][j]表示在子数组[i, j]范围内所能得到的最高的分数,那么最后我们返回dp[0][n-1]就是要求的结果。

那么对于dp[i][j]我们想,如果我们移除boxes[i]这个数字,那么总得分应该是1 + dp[i+1][j],但是通过分析题目中的例子,能够获得高积分的trick是,移除某个或某几个数字后,如果能使得原本不连续的相同数字变的连续是坠好的,因为同时移除的数字越多,那么所得的积分就越高。那么假如在[i, j]中间有个位置m,使得boxes[i]和boxes[m]相等,那么我们就不应该只是移除boxes[i]这个数字,而是还应该考虑直接移除[i+1, m-1]区间上的数,使得boxes[i]和boxes[m]直接相邻,那么我们获得的积分就是dp[i+1][m-1],那么我们剩余了什么,boxes[i]和boxes[m, j]区间的数,此时我们无法处理子数组[m, j],因为我们有些信息没有包括在我们的dp数组中,此类的题目归纳为不自己包含的子问题,其解法依赖于一些子问题以外的信息。这类问题通常没有定义好的重现关系,所以不太容易递归求解。为了解决这类问题,我们需要修改问题的定义,使得其包含一些外部信息,从而变成自包含子问题

那么对于这道题来说,无法处理boxes[m, j]区间是因为其缺少了关键信息,我们不知道boxes[m]左边相同数字的个数k,只有知道了这个信息,那么m的位置才有意义,所以我们的dp数组应该是一个三维数组dp[i][j][k],表示区间[i, j]中能获得的最大积分,当boxes[i]左边有k个数字跟其相等,那么我们的目标就是要求dp[0][n-1][0]了,而且我们也能推出dp[i][i][k] = (1+k) * (1+k)这个等式。那么我们来推导重现关系,对于dp[i][j][k],如果我们移除boxes[i],那么我们得到(1+k)*(1+k) + dp[i+1][j][0]。对于上面提到的那种情况,当某个位置m,有boxes[i] == boxes[m]时,我们也应该考虑先移除[i+1,m-1]这部分,我们得到积分dp[i+1][m-1][0],然后再处理剩下的部分,得到积分dp[m][j][k+1],这里k加1点原因是,移除了中间的部分后,原本和boxes[m]不相邻的boxes[i]现在相邻了,又因为二者值相同,所以k应该加1,因为k的定义就是左边相等的数字的个数。讲到这里,那么DP方法最难的递推公式也就得到了,那么代码就不难写了,需要注意的是,这里的C++的写法不能用vector来表示三维数组,好像是内存限制超出,只能用C语言的写法,由于C语言数组的定义需要初始化大小,而题目中说了数组长度不会超100,所以我们就用100来初始化,参见代码如下:

解法一:

class Solution {
public:
int removeBoxes(vector<int>& boxes) {
int n = boxes.size();
int dp[][][] = {};
return helper(boxes, , n - , , dp);
}
int helper(vector<int>& boxes, int i, int j, int k, int dp[][][]) {
if (j < i) return ;
if (dp[i][j][k] > ) return dp[i][j][k];
int res = ( + k) * ( + k) + helper(boxes, i + , j, , dp);
for (int m = i + ; m <= j; ++m) {
if (boxes[m] == boxes[i]) {
res = max(res, helper(boxes, i + , m - , , dp) + helper(boxes, m, j, k + , dp));
}
}
return dp[i][j][k] = res;
}
};

下面这种写法是上面解法的迭代方式,但是却有一些不同,这里我们需要对dp数组的部分值做一些初始化,将每个数字的所有k值的情况的积分都先算出来,然后在整体更新三维dp数组的时候也很有意思,并不是按照原有的顺序更新,而是块更新,先更新dp[1][0][k], dp[2][1][k], dp[3][2][k]....,再更新dp[2][0][k], dp[3][1][k], dp[4][2][k]...., 再更新dp[3][0][k], dp[4][1][k], dp[5][2][k]....,之前好像也有一道是这样区域更新的题,但是博主想不起来是哪一道了,以后想起来了再来补充吧,参见代码如下:

解法二:

class Solution {
public:
int removeBoxes(vector<int>& boxes) {
int n = boxes.size();
int dp[n][n][n] = {};
for (int i = ; i < n; ++i) {
for (int k = ; k <= i; ++k) {
dp[i][i][k] = ( + k) * ( + k);
}
}
for (int t = ; t < n; ++t) {
for (int j = t; j < n; ++j) {
int i = j - t;
for (int k = ; k <= i; ++k) {
int res = ( + k) * ( + k) + dp[i + ][j][];
for (int m = i + ; m <= j; ++m) {
if (boxes[m] == boxes[i]) {
res = max(res, dp[i + ][m - ][] + dp[m][j][k + ]);
}
}
dp[i][j][k] = res;
}
}
}
return n == ? : dp[][n - ][];
}
};

类似题目:

Burst Balloons

Zuma Game

Strange Printer

参考资料:

https://leetcode.com/problems/remove-boxes/

https://discuss.leetcode.com/topic/84282/memoization-dfs-c

https://discuss.leetcode.com/topic/84687/java-top-down-and-bottom-up-dp-solutions

LeetCode All in One 题目讲解汇总(持续更新中...)

[LeetCode] Remove Boxes 移除盒子的更多相关文章

  1. 546 Remove Boxes 移除盒子

    给定一些不同颜色的盒子,以不同的正整数表示.消去连续相同颜色的盒子,直到全部消除完毕为止.每一次消去可以得到k * k分(k为消去盒子的个数, k  >= 1).计算可以得到的最大得分.注意:盒 ...

  2. [LeetCode] Remove Element 移除元素

    Given an array and a value, remove all instances of that value in place and return the new length. T ...

  3. [LeetCode] Remove 9 移除9

    Start from integer 1, remove any integer that contains 9 such as 9, 19, 29... So now, you will have ...

  4. [LeetCode] Remove Comments 移除注释

    Given a C++ program, remove comments from it. The program source is an array where source[i] is the ...

  5. [Swift]LeetCode546. 移除盒子 | Remove Boxes

    Given several boxes with different colors represented by different positive numbers. You may experie ...

  6. Leetcode 546.移除盒子

    移除盒子 给出一些不同颜色的盒子,盒子的颜色由数字表示,即不同的数字表示不同的颜色.你将经过若干轮操作去去掉盒子,直到所有的盒子都去掉为止.每一轮你可以移除具有相同颜色的连续 k 个盒子(k > ...

  7. Java实现 LeetCode 546 移除盒子(递归,vivo秋招)

    546. 移除盒子 给出一些不同颜色的盒子,盒子的颜色由数字表示,即不同的数字表示不同的颜色. 你将经过若干轮操作去去掉盒子,直到所有的盒子都去掉为止.每一轮你可以移除具有相同颜色的连续 k 个盒子( ...

  8. LeetCode:Remove Duplicates from Sorted List I II

    LeetCode:Remove Duplicates from Sorted List Given a sorted linked list, delete all duplicates such t ...

  9. LeetCode:Remove Duplicates from Sorted Array I II

    LeetCode:Remove Duplicates from Sorted Array Given a sorted array, remove the duplicates in place su ...

随机推荐

  1. Tomcat服务器的常用配置

    1.如何修改端口号, tomcat启动后经常会报端口冲突, 怎么办 如果部署在Linux环境下面, 首先使用netstat -apn命令检查是否是真的端口已经被占用了 如果真的被占用,进入tomcat ...

  2. oracle管理权限和角色

    介绍 这一部分主要看看oracle中如何管理权限和角色,权限和角色的区别在哪里. 当刚刚建立用户时,用户没有任何权限,也不能执行任何操作.如果要执行某种特定的数据库操作,则必需为其授予系统的权限:如果 ...

  3. java基础笔记(6)----面向对象的三大特性

    简介:面向对象的三大特性就是封装,继承,多态,是面向对象的核心. 封装 简介:封装是类的边界,可以对数据起到保护作用 特性:属性私有,提供公开的get/set方法 属性私有:private 数据类型 ...

  4. 如何防止cookie被串改

    在这里我不想多说怎么去操作cookie了,网上博文一大堆,大家可以去自行搜索,在这里也是记录一下自己的知识,以便以后方便查阅.当我们在浏览器地址栏输入地址成功打开网页以后,服务器会把一些信息写入coo ...

  5. "机器人防火墙":人机识别在应用安全及风控领域的一点实践

    美剧 WestWorld 第二集里有个场景十分有意思:游客来到西部世界公园,遇到了一个漂亮的女接待员,但无法区分对方是否是人类,于是产生了如下对话: Guest: "Are you real ...

  6. 『转载』从内存资源中加载C++程序集:CMemLoadDll

    MemLoadDll.h #if !defined(Q_OS_LINUX) #pragma once typedef BOOL (__stdcall *ProcDllMain)(HINSTANCE, ...

  7. 第二次作业--------STEAM

    --------------------------------------第一部分 产品介绍----------------------------------------------------- ...

  8. 201621123062《java程序设计》第十周作业总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常相关内容. 思维导图: 2. 书面作业 本次PTA作业题集异常 2.1. 常用异常 结合题集题目7-1回答 2.1.1 自己以前 ...

  9. C语言--第三周作业

    一.PTA作业中4个题目 1.7-9 A乘以B 要求:输入的两个整数:A是你学号前两位数字,B是你学号后两位数字 a.代码 #include <stdio.h> int main () { ...

  10. 201621123043 《Java程序设计》第11周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多线程相关内容. 2. 书面作业 本次PTA作业题集多线程 1. 源代码阅读:多线程程序BounceThread 1.1 BallR ...