LeetCode题解:546-移除盒子

题目地址:

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

题目描述:

给出一些不同颜色的盒子,盒子的颜色由数字表示,即不同的数字表示不同的颜色。
你将经过若干轮操作去去掉盒子,直到所有的盒子都去掉为止。每一轮你可以移除具有相同颜色的连续 k 个盒子(k >= 1),这样一轮之后你将得到 k*k 个积分。
当你将所有盒子都去掉之后,求你能获得的最大积分和。

示例:

数据范围:

题目分析:

首先声明题目分析借鉴了官方题解,在此基础上结合了自己的理解。这道题需要我们多读几遍题才能明白需要我们做什么。要注意,这里的数字表示的是类别而不是大小,题目中关注的重点在于移除尽可能长的连续相同内容的子串,或者是通过移除获得尽可能长的子串(连续的相同数字为一个子串)。

贪心的策略并不能给我们带来最好的结果,因为贪心策略并没有考虑移除某部分数字之后会导致原先没有连在一块的数字又连接在一块,而恰恰是这种连接导致最终结果变大。以示例中的数组为例,如果是采用每次都删除尽量长的子串,那么删除的过程如下,结果小于示例中给出的23,所以不可取。

假定当前考虑的子串的左右边界下标为 [L, R],显然在考虑当前如何删除[L,R]范围中的数字时,还要考虑之前操作的结果,例如上面的例子中,第一步删除了3个连续的‘2’之后,导致原先两个‘3’连在了一块,此时删除‘4’可以让3个‘3’连在一块,取得更好的结果,而不是贪图当前仅有的两个连续的‘3’。

示例中给的例子略显简单,难以支撑后面的分析,现在让我们换个稍微复杂点的序列:[6,3,6,5,6,7,6,6,8,6],存储在名为boxes的数组中。

假定序列第一个位置下标为1,并且我们删除程序进行到了某一个状态:删除了后半段的‘7’和‘8’。此时剩余序列为:{ [6,3,6,5,6],6,6,6 }。我们现在考虑L=1,R=5,即[1,5]范围内的子序列,此时根据是否考虑直接删除子序列[6,3,6,5,6]之外的3个‘6’而分出两个策略。在进行详细分析之前,我们需要使用数学符号描述我们所要使用的状态,假定f(L,R,K)表示移除区间[L,R]中的元素以及区间右边K个数值为 boxes[R] 的元素所得到的最大值。下面分析一下两个策略。

策略一,莽,管他娘的,先删除已有的连续值再说:

此时的状态转移方程为 f(1,5,3) = f(1,4,0) + 4*4,右边有K+1个连续的数字,可以先计算删除这些数字的结果,然后再向内考虑区间[1, 4]中的内容。

策略二,先怂一波,考虑一下能不能让连续的‘6’的长度变得更长一些,毕竟区间[1,5]中还有3个‘6’:

可以将区间【1,5】右端的6看做是区间的叛徒,身在区间内,却向往和区间外的小伙伴团聚,并且在走之前要号召区间内同为‘6’的伙伴一块离开。依次遍历区间内的数字,每次遇到和自己同值(即都是6)的数时,删除夹在中间的杂鱼(如何删除呢,当然不是直接删,而是使用递归计算得到值)这样每次都可得到两个连续的‘6’,加上原先外面的3个‘6’,那实际上就是5个连续的‘6’。遍历的下标用i表示,从区间左端L起始,当i=1时,boxes[1] == boxes[5],“删除”中间的3个杂鱼,之后得到的5个连续的‘6’,此时按照约定的状态,状态转移方程为 f(1, 5, 3) = f(1, 1, 4) + f(2, 4, 0)。 继续遍历,同理当i=3时,值为6,删除中间夹着的‘5’就可以得到连续的‘6’,同样的,状态转移方程为 f(1, 5, 3) = f(1, 3, 4) + f(4, 4, 0)。直到 i==R-1,遍历结束。遍历中肯定会遇到多个和boxes[R]值相同的情况,只需要取最大的那个即可。

对于策略1和策略2,同样需要取值最大的那个。

代码

    public int removeBoxes(int[] boxes) {

        int[][][] dp = new int[100][100][100];
        return calculatePoints(boxes, dp, 0, boxes.length-1,0);
    }

    private 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 ((L<R) && 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, i+1, R-1, 0)+calculatePoints(boxes, dp, L, i, K+1));
            }
        }
        return dp[L][R][K];
    }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值