LeetCode 546. 移除盒子 | Python

需求狂
• 阅读 1610

546. 移除盒子


题目


给出一些不同颜色的盒子,盒子的颜色由数字表示,即不同的数字表示不同的颜色。
你将经过若干轮操作去去掉盒子,直到所有的盒子都去掉为止。每一轮你可以移除具有相同颜色的连续 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 分)

提示:

  • 1 <= boxes.length <= 100
  • 1 <= boxes[i] <= 100

解题思路


思路:动态规划

首先先看题目,题目给定一组序列,里面不同的数字代表着不同颜色的盒子。题目要求移除盒子,求得序列为空,也就是移除掉所有盒子时能获得的最大积分。

在这里,积分的计算方式如下(移除盒子的操作次数不限):

当移除的盒子是具有相同颜色的,假设为 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 分)

这里大致说下解释中的部分:

  • 首先移除的是 32,积分为 3*3=9;
  • 再是移除的是 14,积分为 1*1=9;
  • 然后移除的是 33,积分为 3*3=9;
  • 最后移除的是 21,积分为 2*2=4。

在这里,我们可以看到当移除某个颜色的盒子之后,序列会发生变化,也就说,原本并非相连的盒子,后续也会变成连续的相同颜色盒子。

那么现在的问题就是,如何找到能够在移除盒子后,构成连续相同的盒子,从而获得更多积分的策略。

状态定义
参考官方题解,默认在区间 [l, r] 消除 boxes[r]

前面说明了,因为移除盒子后,对当前的序列是有影响的。那么我现在就不能单纯的设定 dp[l][r] 表示移除区间 [l, r] 内盒子能获得的最大积分。在这里,我们还需要一个参数 k 来标记状态,这个 k 表示的后面存在 k 个与 boxes[r] 相同颜色的盒子个数。

那么设 dp[l][r][k] 表示从区间 [l, r] 移除相同颜色的盒子, r 右边有 k 个和 boxes[r] 相同颜色的盒子的积分。

下面用图来说明下,有助于理解。假设拥有以下序列。

LeetCode 546. 移除盒子 | Python

现在假设先将数字 4 代表的盒子移除,如下:

LeetCode 546. 移除盒子 | Python

那么,就剩下部分序列中,对于移除数字 2 代表的盒子,我们有两个策略:

    1. 将此时 3 个连接的盒子(数字 2 代表的盒子)先移除掉;
    1. 将此时 3 个盒子先当做整体,删除数字 3 代表的盒子,与前面相同颜色的盒子组合。

此时,序列如下:

LeetCode 546. 移除盒子 | Python

先看策略一,移除此时相连颜色相同的 3 个盒子(数字 2)

LeetCode 546. 移除盒子 | Python

那么剩下序列如下:

LeetCode 546. 移除盒子 | Python

此时上面的序列积分表现方式为:dp[0][3][0],也就说当前策略的积分为:dp[0][3][0] + 3x3

再看第二种策略,将后面连续的当成整体,在前面找到与 boxes[r] 颜色相同的盒子,移除掉中间的盒子。

LeetCode 546. 移除盒子 | Python

下面红色虚线部分,表示找到与 boxes[r] 相同的盒子

LeetCode 546. 移除盒子 | Python

那么现在将中间黄色部分的盒子移除,对应能获得的积分为 dp[3][3][0]

LeetCode 546. 移除盒子 | Python

LeetCode 546. 移除盒子 | Python

此时剩下的序列为 dp[0][2][3]

LeetCode 546. 移除盒子 | Python

那么此时策略二的积分为:dp[0][2][3] + dp[3][3][0]

比较两个策略,去积分值大的策略积分所得。

状态转移方程

现在,就上面的图示分析进行总结:

  • 策略一:

    • 在区间 [l, r] 中,先移除右边与 boxes[r] (包括 boxes[r])相同的 k+1 个盒子,然后在考虑移除区间 [l, r-1] 的盒子;
    • 那么此时的转移方程为:dp[l][r][k] = dp[l][r-1][0] + (k+1)*(k+1)
  • 策略二:

    • 在区间 [l, r] 之间,将 boxes[r] 这个盒子与后面相同颜色的盒子当成是一个整体。然后在 [l, r-1] 这个区间中进行遍历,找到与 boxes[r] 相同颜色的盒子,假设在位置 x 找到这样的盒子,那么将 [x+1, r-1] 这个区间的盒子都移除掉,然后再考虑移除 [l, x] 这个区间的盒子;
    • 那么此时的状态转移方程为:dp[l][r][k] = dp[x+1][r-1][0] + dp[l][x][k+1]

具体代码实现如下。

代码实现


class Solution:
    def removeBoxes(self, boxes: List[int]) -> int:
        # 定义 dp
        n = len(boxes)
        dp = [[[0] * n for _ in range(n)] for _ in range(n)]

        def cacl_point(boxes, l, r, k):
            """计算积分
            Args:
                boxes: 序列
                l: 序列的起始位置
                r: 序列的结束位置
                k: r 后面与 boxes[r] 相同颜色盒子的个数
            Returns:
                返回序列中策略的最大积分
            """
            if l > r:
                return 0
            
            # 防止重复计算
            if dp[l][r][k] != 0:
                return dp[l][r][k]
            
            # 首先先找与 boxes[r] 相同颜色的盒子
            while l < r and boxes[r] == boxes[r-1] :
                r -= 1
                k += 1
            
            # 策略一(描述见文章)
            dp[l][r][k] = cacl_point(boxes, l, r-1, 0) + (k+1)*(k+1)
            # 策略二(描述见文章)
            for x in range(l, r-1):
                if boxes[x] == boxes[r]:
                    # 这里直接比较出较大值,维护更新
                    dp[l][r][k] = max(dp[l][r][k], cacl_point(boxes, x+1, r-1, 0)+cacl_point(boxes, l, x, k+1))
            return dp[l][r][k]

        return cacl_point(boxes, 0, n-1, 0)

实现结果


LeetCode 546. 移除盒子 | Python

欢迎关注


公众号 【书所集录

点赞
收藏
评论区
推荐文章
晴空闲云 晴空闲云
3年前
css中box-sizing解放盒子实际宽高计算
我们知道传统的盒子模型,如果增加内边距padding和边框border,那么会撑大整个盒子,造成盒子的宽度不好计算,在实务中特别不方便。boxsizing可以设置盒模型的方式,可以很好的设置固定宽高的盒模型。盒子宽高计算假如我们设置如下盒子:宽度和高度均为200px,那么这会这个盒子实际的宽高就都是200px。但是当我们设置这个盒子的边框和内间距的时候,那
Souleigh ✨ Souleigh ✨
4年前
几个有点意思的 CSS 技巧
如果你是一名前端开发人员或者想成为一名开发人员,那么,我今天与你分享的9个CSS技巧,你需要知道一下。现在,我们开始吧。1、学习盒子模型你在学习CSS时,应该避免使用Bootstrap或TailwindCSS等框架,这些工具非常适合构建漂亮的网站,但如果你还不能正确的了解CSS,则建议不要使用这些框架中的任何一个。因为如果你使用了这些工具,你将无法学习
爱写码 爱写码
4年前
为RPC而生的t-io企业集群版的msg服务器tio-msg-demo你应该感兴趣
概念解释什么是RPC(RemoteProcedureCall)远程过程调用,是一种通过网络从远程计算机程序上请求服务,实现某个业务,但是不需要具体了解底层网络技术的协议。tio把程序中对外实现通信的各个协议模块进行了打包处理成一个盒子,上层应用对外通信就只要对接盒子的接口,而不必关心盒子里面的内容,RPC服务要对外实现远程调用,首先要跟tio通信,再到远
菜园前端 菜园前端
2年前
你真的了解的CSS3盒模型和CSS3特性知识吗
原文链接:什么是CSS3?CSS3是CSS一个新的标准,直接理解为是CSS的升级版,里面新增了很多样式(特性)。CSS3盒子模型:::warningCSS3盒子模型必须要掌握,否则你在实际开发中遇到样式错乱很难排查问题。:::旧版的IE浏览器与其它浏览器解
菜园前端 菜园前端
2年前
考考你HTML常用的标签有哪些?
原文链接:基础标签div块元素介绍:没有任何含义,主要用于div进行模块布局类型:块级元素block,盒子占用宽度为一整行属性:没有属性language我是模块span行内文本元素介绍:没有任何含义,主要用于展示文本内容类型:内联元素inline,盒子占用
LinMeng LinMeng
5年前
css之元素居中
行内元素居中文本垂直居中单行文本垂直居中设置lineheight与盒子高度一样就行这里有一个误区,我经常在设置单行文本居中的时候,会习惯性的设置height属性与linheight属性一样,其实完全没必要,只设置lineheight就行,这时候盒子的高度由lineheight撑起来,与height完全相同。多行文本垂直居中1.ve
Wesley13 Wesley13
4年前
HTML5中新增的布局标签
1.1.1  盒子模型层次盒子模型的层次遵循以下顺序:内容paddingà边框àbackgroundimageàbackgroundcoloràmargin!(https://oscimg.oschina.net/oscnet/722909c8dcfd273c73b0c6534e20f8d9bd4.gif)!(ht
Wesley13 Wesley13
4年前
HTML中经常用到的对齐,居中方式
在编写一片网页时,我们经常需要使一些文本或者图片,盒子居中!但是在众多的写法里,那些才能使我们的目的最快,最有效的达到呢!居中也是有轴线之分的,水平轴,垂直于水平轴的轴,交叉轴。1盒子居中margin:auto;通常在这此行只有一个盒子的情况下使用\需要定宽\常规流和浮动不用\2文本居中   定义水平轴线对齐方式flexst
Stella981 Stella981
4年前
DragonBonesPro动画制作——补间动画、龙骨动画
开发工具:DragonBonesPro1.开场动画2.小丑盒子3.跑步的人4.跳跳羊一.开场动画1.导入素材!(https://oscimg.oschina.net/osc
Stella981 Stella981
4年前
HTML前端开发之路——盒子背景属性
1.backgroundclip属性简介backgroundclip属性用于设置盒子背景的一个显示区域,分别有borderbox,paddingbox,contentbox;borderbox表明背景是从边框开始,即包含边框;paddingbox表明背景是从内边距开始,不包含边框;
Wesley13 Wesley13
4年前
CSS 清除浮动的4种方法
1\.为什么要清除浮动因为父级盒子很多情况下,不方便给高度,但是子盒子浮动就不占有位置,最后父级盒子高度为0,就影响了下面的标准流盒子。!在这里插入图片描述(https://imgblog.csdnimg.cn/20201111221816518.pngpic_center)