@(labuladong的算法小抄)[dp]
leetcode 354. 俄罗斯套娃信封问题
题目描述
解题思路
参考:labuladong的算法小抄P104
常规dp
为什么在按宽度升序排列后,判断时仍然需要宽度严格大于?
因为宽度存在相同的情况,所以还需要判断。
class Solution {
public int maxEnvelopes(int[][] envelopes) {
int n = envelopes.length;
if (n == 0) return 0;
/* 定义:以envelopes[i]为结尾的最大信封,最多能有dp[i]个信封能组成套娃信封 */
int[] dp = new int[n];
Arrays.fill(dp, 1);
/* base case */
dp[0] = 1;
/* 按宽度升序排列,宽度相同时,按高度降序排列 */
Arrays.sort(envelopes, (int[] a, int[] b) -> (a[0] == b[0] ? b[1] - a[1] : a[0] - b[0]));
for (int i = 1; i < n; i++) {
for (int j = 0; j < i; j++) {
/* 只有宽度和高度都大于时才能套娃 */
if (envelopes[i][0] > envelopes[j][0] && envelopes[i][1] > envelopes[j][1])
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
int res = 0;
for (int i = 0; i < n; i++)
res = Math.max(res, dp[i]);
return res;
}
}
复用最长递增子序列LIS
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xALwJRi4-1614665869859)(./1608345013050.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KWuT8lbQ-1614665869861)(./1608345026893.png)]
先对宽度w进行升序排列,如果遇到w相同的情况,则按照高度h降序排列。之后把所有的h作为一个数组,在这个数组上计算出的LIS的长度就是答案。
如果遇到w相同的情况,为什么按照高度h降序排列?
因为两个w相同的信封不能相互包含,w相同时将h逆序排列,则这些逆序h中最多只会有一个被选入递增子序列,保证了最终的信封序列中不会出现w相同的情况。
class Solution {
public int maxEnvelopes(int[][] envelopes) {
int n = envelopes.length;
if (n == 0) return 0;
/* 按宽度升序排列,如果宽度一样,则按高度降序排列 */
Arrays.sort(envelopes, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[0] == o2[0] ? o2[1] - o1[1] : o1[0] - o2[0];
}
});
/* 对高度数组求LIS */
int[] height = new int[n];
for (int i = 0; i < n; i++)
height[i] = envelopes[i][1];
return lengthOfLIS(height);
}
/* 求一个序列的最长上升子序列的长度 */
public int lengthOfLIS(int[] nums) {
if (nums.length == 0) return 0;
/* 定义:dp[i]表示以nums[i]结尾的最长上升子序列的长度 */
int[] dp = new int[nums.length];
/* base case */
Arrays.fill(dp, 1);
for (int i = 1; i < nums.length; i++) {
for (int j = 0; j < i; j++) {
if (nums[i] > nums[j])
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
int res = -1;
for (int i = 0; i < dp.length; i++) {
res = Math.max(res, dp[i]);
}
return res;
}
}