给定一些标记了宽度和高度的信封,宽度和高度以整数对形式 (w, h) 出现。当另一个信封的宽度和高度都比这个信封大的时候,这个信封就可以放进另一个信封里,如同俄罗斯套娃一样。
请计算最多能有多少个信封能组成一组“俄罗斯套娃”信封(即可以把一个信封放到另一个信封里面)。
说明: 不允许旋转信封。
示例:
输入: envelopes = [[5,4],[6,4],[6,7],[2,3]]
输出: 3
解释: 最多信封的个数为 3, 组合为: [2,3] => [5,4] => [6,7]。
解题思路
因为下一个信封能符合要求的话,必须宽度和高度都比之前的大。第一时间想到把宽度排序。比如例子中的信封顺序就变成了:
[[2,3],[5,4],[6,4],[6,7]]
这样从前往后,比如宽度会越来越大。那就只需要考虑在这个顺序下,高度从小到大能取到最长的数量是多少,也就是
[3,4,4,7]
保持递增的话,最长的子数列是多少。 这个求取方法可以用动态规划的方法来求,即求出每个位置开始的子列上,能取到最长的递增子列长度是多少,然后求取最大值:
const dp = new Array(n).fill(1);
for (let i = 0; i < arr.length; i++) {
for (let j = 0; j < i; j++) {
if (arr[j] < arr[i]) dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
但是有可能有宽度相同的情况,比如例子变成:
[[2,3],[5,4],[6,5],[6,7]]
这样出来的数组可能就是
[3,4,5,7]
会发现最长递增数组的长度是4,但是因为5 和 7 的信封的宽度都是 6,只能选一个,所以就会带入一个错误的答案。
解决方法是把每个宽度相同的信封的高度倒序排列:
[[2,3],[5,4],[6,7],[6,5]]
这样出来的高度数组是:
[3,4,7,5]
就可以保证在考虑一个高度值作为目标求最长子序列的时候,相同宽度里的其他高度值就自动被过滤掉了。
完整代码:
/**
* @param {number[][]} envelopes
* @return {number}
*/
var maxEnvelopes = function(envelopes) {
const n = envelopes.length;
if (n === 0) return 0;
const sortedArr = envelopes.sort((e1, e2) => {
if (e1[0] !== e2[0]) return e1[0] - e2[0];
return e2[1] - e1[1];
}).map(e => e[1]);
const dp = new Array(n).fill(1);
for (let i = 0; i < n; i++) {
for (let j = 0; j < i; j++) {
if (sortedArr[j] < sortedArr[i]) dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
return Math.max.apply(null, dp);
};
总结
这道题看起来有些摸不到头脑,但是仔细分析一下就可以固定其中一个维度,简化成另外一个问题。前置知识是求数组的递增子数组,需要更熟悉动态规划求解问题的方式。
本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情