难度困难
给定一些标记了宽度和高度的信封,宽度和高度以整数对形式 (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;
}
}