力扣动态规划 546. 移除盒子

  • Post author:
  • Post category:其他


给出一些不同颜色的盒子,盒子的颜色由数字表示,即不同的数字表示不同的颜色。

你将经过若干轮操作去去掉盒子,直到所有的盒子都去掉为止。每一轮你可以移除具有相同颜色的连续 k 个盒子(k >= 1),这样一轮之后你将得到 k*k 个积分。

当你将所有盒子都去掉之后,求你能获得的最大积分和。

示例 1:

输入:

[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 分)

提示:盒子的总数 n 不会超过 100。

来源:力扣(LeetCode)

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

方法 1:暴力方法【超时】

算法

暴力方法是显然的,我们试图移除每一个可能的元素,并计算得分以及剩下的序列继续递归。

最后,dpdp 的更新式为:\text{dp[l][r][k]} = max(\text{dp[l][r][k]}, \text{dp[l][i][k+1]} + \text{dp[i+1][r-1][0]})dp[l][r][k]=max(dp[l][r][k],dp[l][i][k+1]+dp[i+1][r-1][0])。

最后,dp[0][n-1][0] 就是最后的结果。实现如下,使用 calculatePoints 函数用递归更好的计算结果。

Java


public class Solution {
    public int removeBoxes(int[] boxes) {
        return remove(boxes);
    }
    public int remove(int[] boxes)
    {
        if(boxes.length==0)
            return 0;
        int res=0;
        for(int i=0,j=i+1;i<boxes.length;i++)
        {
            while(j<boxes.length && boxes[i]==boxes[j])
                j++;
            int[] newboxes=new int[boxes.length-(j-i)];
            for(int k=0,p=0;k<boxes.length;k++)
            {
                if(k==i)
                    k=j;
                if(k<boxes.length)
                    newboxes[p++]=boxes[k];
            }
            res=Math.max(res,remove(newboxes)+(j-i)*(j-i));
        }
        return res;
    }
}


复杂度分析

时间复杂度:O(n!),f(n) 是找到 n个盒子有 nn 种不同颜色的方案,显然 f(n-1)f(n)=n×f(n−1),所以结果 n! 是时间复杂度。

空间复杂度:O(n^2)

方法 2:记忆化搜索


算法

记忆化搜索

令dp[i][j]表示从j开始,长度为i的字符串。

dp[i][j] = Math.max(dp[i-1][j] + 1, 最后一个字符与前面任意多个相同字符联合起来得分的最大值);

所以对于dp,需要使用深度搜索找到 最后一个字符,与前面任意多个字符联合起来的最大得分。

Java

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];
    }
}


复杂度分析

时间复杂度:O(n^4)要填满所有 dp 数组。

空间复杂度:O(n^3),dp 的数组大小。



版权声明:本文为doublekillyeye原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。