当前位置: 代码迷 >> 综合 >> 546. Remove Boxes 移除盒子
  详细解决方案

546. Remove Boxes 移除盒子

热度:50   发布时间:2024-02-11 07:07:24.0

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

动态规划

首先,假设我们拿到的 boxes 长这样:

图片1.png

假设我们经过一系列操作后消掉了其中一些盒子:
图片2.png

对于这样一个子序列,我们就可以记录到dp数组里。我们可以操作的范围是lr的子序列。由于我们对每个子序列默认都是点击boxes[r]来消除,因此要知道r的后面还连着几个与boxes[r]相同颜色的盒子,记为k。如下图,l = 0, r = 6, k = 2, 将其能获得的最高得分记在dp[0][6][2]

图片19.png

现在我们调用 calculatePoints(i, r, k) 来计算它的最高得分 dp[i][r][k]

calculatePoints(i, r, k)

在我们这个子序列中,dp[0][6][2]dp[0][5][3] 实际上是等价的。我们将r向左一直移动到不能再移动为止。

图片16.png

接着,我们计算出不同策略的得分,取最高分。

策略 1

我们可以直接点boxes[r],把最后4个盒子一次性消除,获得16分!

图片5.png

剩下的盒子成为这样一个子序列 dp[0][4][0]

图片12.png

策略1得分:4*4 + dp[0][4][0]

策略 2

我们还可以把夹在中间的杂鱼盒子都消掉,让后面连起来的盒子数更多:

图片8.png

为了找到可以跟 boxes[r] 连起来的盒子,令 i = l

图片21.png

i++

图片22.png

直到 boxes[i] == boxes[r],就说明我们搜索到了

图片23.png

在这个例子中,消掉杂鱼盒子能获得的分数是 dp[3][4][0]

图片15.png

剩下的盒子的得分是 dp[0][2][4]

图片14.png

综上,策略2得分:dp[0][2][4] + dp[3][4][0]

总结

为了取得一个子序列的最高得分,我们分不同策略,每种策略的得分可以看作是1~2个子子序列的最高分之和。

图片20.png

在这里插入图片描述

Code

	def removeBoxes(self, boxes: List[int]) -> int:def dp(l, r, n):nonlocal memo, boxesif memo.get((l, r, n)):return memo[(l, r, n)]if l == r - 1:return (n + 1) * (n + 1)if boxes[l] == boxes[l + 1]:return dp(l + 1, r, n + 1)res = (n + 1) * (n + 1) + dp(l + 1, r, 0)for l2 in range(l + 2, r):if boxes[l2] == boxes[l]:res = max(res, dp(l + 1, l2, 0) + dp(l2, r, n + 1))memo[(l, r, n)] = resreturn resmemo = {}return dp(0, len(boxes), 0)

复杂度分析

  • 时间复杂度:O(n4)O(n^4)O(n4)。最坏情况下每个 f(l,r,k)f(l, r, k)f(l,r,k) 被计算一次,每次状态转移需要 O(n)O(n)O(n) 的时间复杂度。
  • 空间复杂度:O(n3)O(n^3)O(n3)dp\rm dpdp 数组的空间代价是 O(n3)O(n^3)O(n3),递归使用栈空间的代价为 O(n)O(n)O(n)
  相关解决方案