leetcode—354. 俄罗斯套娃信封问题

3 篇文章 0 订阅

354. 俄罗斯套娃信封问题

难度困难

给定一些标记了宽度和高度的信封,宽度和高度以整数对形式 (w, h) 出现。当另一个信封的宽度和高度都比这个信封大的时候,这个信封就可以放进另一个信封里,如同俄罗斯套娃一样。

请计算最多能有多少个信封能组成一组“俄罗斯套娃”信封(即可以把一个信封放到另一个信封里面)。

说明:
不允许旋转信封。

示例:

输入: envelopes = [[5,4],[6,4],[6,7],[2,3]]
输出: 3 
信封最多的个数为3, 组合为: [2,3] => [5,4] => [6,7]

这道题和leetcode—300. 最长递增子序列,dp(动态规划)第一题很像,只是从一维数组变成了二维数组。

先说一下解题思路:

首先我们将所有的信封按照 w 值升序、h 值降序进行排序;

随后我们就可以忽略 w 维度,求出 h 维度的最长严格递增子序列,其长度即为答案。

那为什么思路是这样的呢?还得看力扣官方题解为我们进行解答:

同时控制 w 和 h 两个维度并不是那么容易,因此我们考虑固定一个维度,再在另一个维度上进行选择。例如,我们固定 w 维度,那么我们将数组envelopes 中的所有信封按照 w 升序排序。

这样一来,我们只要按照信封在数组中的出现顺序依次进行选取,就一定保证满足:

然而小于等于和小于还是有区别的,但我们不妨首先考虑一个简化版本的问题:

在 w值互不相同的前提下,小于等于 和小于是等价的,那么我们在排序后,就可以完全忽略 w 维度,只需要考虑 h 维度了。此时,我们需要解决的问题即为:

给定一个序列,我们需要找到一个最长的子序列,使得这个子序列中的元素严格单调递增,即上面要求的:

当我们解决了简化版本的问题之后,我们来想一想使用上面的方法解决原问题,会产生什么错误。当 w 值相同时,如果我们不规定 h 值的排序顺序,那么可能会有如下的情况:

排完序的结果为 [(w,h)]=[(1,1),(1,2),(1,3),(1,4)],由于这些信封的 w 值都相同,不存在一个信封可以装下另一个信封,那么我们只能在其中选择 1 个信封。然而如果我们完全忽略 w 维度,剩下的 h 维度为 [1,2,3,4],这是一个严格递增的序列,那么我们就可以选择所有的 4 个信封了,这就产生了错误。

因此,我们必须要保证对于同一个w 值,我们最多只能选择 1 个信封。

我们可以将 h 值作为排序的第二关键字进行降序排序,这样一来,对于每一种 w 值,其对应的信封在排序后的数组中是按照 h 值递减的顺序出现的,那么这些 h 值不可能组成长度超过 1 的严格递增的序列,这就从根本上杜绝了错误的出现。

因此我们就可以得到解决本题需要的方法:

首先我们将所有的信封按照 w 值第一关键字升序、h 值第二关键字降序进行排序;

随后我们就可以忽略 w 维度,求出 h 维度的最长严格递增子序列,其长度即为答案。

代码:

class Solution {
            
    public int maxEnvelopes(int[][] envelopes) {
        if(envelopes.length == 0){
            return 0;
        }
                                
        int n = envelopes.length;
        Arrays.sort(envelopes,(a,b) -> {//按照宽度进行升序排序,如果宽度一致,则按高度降序排列
            if(a[0] == b[0]){
                return b[1]-a[1];//表示对第二个元素降序
            }
            return a[0]-b[0];//表示对第一个元素升序
        });
        int[] dp = new int[n];
        Arrays.fill(dp,1);
        int res=1;
        for(int i=1;i<n; i++){
            for(int j=0; j<i; j++){
                if(envelopes[j][1] < envelopes[i][1]){//因为事先按照宽度进行了排序,只需要求根据高度的最长递增子序列了
                    dp[i] = Math.max(dp[i],dp[j]+1);
                }
            }
            res = Math.max(res,dp[i]);
        }
        return res;

    }
}

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值