首页 > 试题广场 >

信封嵌套问题

[编程题]信封嵌套问题
  • 热度指数:4319 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
给 n 个信封的长度和宽度。如果信封 a 的长和宽都小于信封 b ,那么信封 a 可以放到信封 b 里,请求出信封最多可以嵌套多少层。

数据范围:
要求:空间复杂度 ,时间复杂度
进阶:空间复杂度 ,时间复杂度
示例1

输入

[[3,4],[2,3],[4,5],[1,3],[2,2],[3,6],[1,2],[3,2],[2,4]]

输出

4

说明

从里到外分别是{1,2},{2,3},{3,4},{4,5}。       
示例2

输入

[[1,4],[4,1]]

输出

1

备注:
时间复杂度O(nlog n),空间复杂度O(n)。
经典的动态规划做法就不多说了,当确定以letters[i][1]结尾的序列时,要将它拼在之前最长的递增序列末尾。但是哪个序列最长?我们不得而知,因此会存在向数组左边遍历的行为,从而造成O(n2)的时间复杂度。
要把O(n2)的时间复杂度优化到O(nlog2n),一个直观的想法就是要把上面提到的遍历行为省掉,如此一来就需要构建单调性,利用二分查找来优化。这里我们弃用之前的dp数组,使用一个新的数组ends,数组中填过数的区域表示有效区域,有效区域中的元素ends[i]表示长度为i+1的最长递增子序列中的最小结尾。很显然这个ends数组是个单调递增的数组,因为每个位置都是给定长度下的最小结尾,如果长度L2>L1,那么更长的最长递增子序列的结尾一定比短的那个子序列的最小结尾大。所以当我们遍历到信封i时,就可以在ends数组的有效区域内进行二分查找,寻找第一个大于等于letters[i][1]的位置,如果找到了位置x,说明letters[i][1]应该作为长度为x+1的最长递增子序列的更小结尾放在此处;如果没找到就直接将letters[i][1]放在ends数组有效区域的下一个位置,表示此时添加上信封i,能够使得最长递增子序列长度增加1,ends数组的有效区域扩充一个元素。
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param letters int二维数组 
     * @return int
     */
    public int maxLetters (int[][] letters) {
        // write code here
        Arrays.sort(letters, (a, b) -> a[0] != b[0]? a[0] - b[0]: b[1] - a[1]);
        int n = letters.length;
        int[] ends = new int[n];       // ends[i]表示长度为i+1的递增子序列最小结尾
        ends[0] = letters[0][1];
        int maxLen = 1, tail = 0;      // 最长递增子序列长度与ends数组的有效区末尾下标
        for(int i = 1; i < n; i++){
            int index = lowerbound(ends, 0, tail, letters[i][1]);
            ends[index] = letters[i][1];
            if(index > tail){
                // ends数组的有效区没能找到第一个大于等于letters[i][1]的区域,就扩充ends数组有效区
                tail ++;
            }
            maxLen = Math.max(maxLen, index + 1);
        }
        return maxLen;
    }
    
    private int lowerbound(int[] arr, int L, int R, int target) {
        int left = L, right = R, idx = R + 1;
        while(left <= right){
            int mid = left + ((right - left) >> 1);
            if(arr[mid] < target){
                left = mid + 1;
            }else{
                idx = mid;
                right = mid - 1;
            }
        }
        return idx;
    }
}

发表于 2021-12-11 09:40:47 回复(0)
这道题的思路是:
1:先排序,排序规则是:先长度升序,再宽度降序
2:再将宽度看为一维数组,求最大子序列,长度即为本题的最大嵌套信封;
import java.util.*;


public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param letters int二维数组
* @return int
*/
public int maxLetters (int[][] letters) {
// write code here
if(letters==null||letters.length<1){return 0;};
if(letters.length==1){return 1;};
Arrays.sort(letters,new Comparator<int[]>(){
@Override
public int compare(int[] o1,int[]o2){
if(o1[0]==o2[0]){
return o2[1]-o1[1];
}else{
return o1[0]-o2[0];
}
}
});
int[]dp =new int[letters.length];
dp[0]=1;
int maxNcoun=0;
for(int i=0;i<letters.length;i++){
int left=0,right=maxNcoun;
while(left<right){
int mid=(left+right)>>1;
if(dp[mid]>=letters[i][1]){
right=mid;
}else{
left=mid+1;
}
}
dp[left]=letters[i][1];
if(left==maxNcoun)maxNcoun++;
// maxNcoun=Math.max(maxNcoun,max);
}
return maxNcoun;
}
}


发表于 2021-08-13 23:17:47 回复(0)
public int maxLetters (int[][] letters) {

        Arrays.sort(letters, new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                if (o1[0]== o2[0]) {
                    return o1[1] - o2[1];
                } else {
                    return o1[0] - o2[0];
                }
            }
        });
        int [] dp = new int[letters.length];
        dp[0] = 1;
        for (int i = 1;i < letters.length;i++) {
            for (int j = 0;j < i;j++) {
                if (letters[i][0] > letters[j][0] && letters[i][1] > letters[j][1]) {
                    dp[i] = Math.max(dp[i],dp[j] + 1);
                } else {
                    dp[i] = Math.max(dp[i],dp[j]);
                }
            }
        }

        return dp[letters.length - 1];
    }
发表于 2021-07-20 18:20:51 回复(0)

问题信息

上传者:牛客301499号
难度:
3条回答 2853浏览

热门推荐

通过挑战的用户

查看代码