【俄罗斯套娃信封问题】【堆积木】

33 篇文章 0 订阅
10 篇文章 0 订阅

俄罗斯套娃信封问题

https://leetcode-cn.com/problems/russian-doll-envelopes/

 给定一些标记了宽度和高度的信封,宽度和高度以整数对形式 (w, h) 出现。当另一个信封的宽度和高度都比这个信封大的时候,这个信封就可以放进另一个信封里,如同俄罗斯套娃一样。
 请计算最多能有多少个信封能组成一组“俄罗斯套娃”信封(即可以把一个信封放到另一个信封里面)。
说明:
 不允许旋转信封。
示例:
输入:

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

代码

思路1:动态规划
以envelopes =[[4,5],[4,6],[6,7],[2,3],[1,1]]为例;

  • 1.先将envelopes的长度进行从小到大排序,在长度相等的情况下,对它的宽度也进行从小到大排序得到 [1,1],[2,3],[4,5],[4,6],[4,7]

Arrays.sort(envelopes, new Comparator<int[]>(){
   public int compare(int[] o1,int [] o2){
    //判断第一个元素是否相等
     if (o1[0] == o2[0]) {
      return o1[1] - o2[1];
     } else {
      return o1[0] - o2[0];
    }
    }
  });

  • 2.因为f[i]表示第i个信封可以嵌套的最大信封数,结果要求最多有多少个信封,最多信封为嵌套信封加本身的信封,故将其每个位置初始化为1。
  • 3.for语句嵌套,如果第i个信封的长度和宽度都大于第j个信封,则可以把它嵌套到i里面信封里面,所以此时第i个信封可以嵌套的最大信封数为f[i] = Math.max(f[i],f[j]+1);
  • 4.最多的信封数即为f[]里面存在的最大的数。返回结果。

完整代码:

class Solution {
    public static int maxEnvelopes(int[][] envelopes) {
        // write your code here
        if(envelopes==null||envelopes.length==0){
            return 0;
        }
        //长度从小到大排序,在长度相等的情况下,高度从小到大排序
        Arrays.sort(envelopes, new Comparator<int[]>(){
            public int compare(int[] o1,int [] o2){
                //判断第一个元素是否相等
                if (o1[0] == o2[0]) {
                    return o1[1] - o2[1];
                } else {
                    return o1[0] - o2[0];
                }
            }
        });
        int n=envelopes.length;
        //f[i]表示第i个信封可以嵌套的最大信封数
        //5个信封的初始化
        int[] f=new int[n];
        int res=0;
        for(int i=0;i<n;i++){
            f[i]=1;
        }
        for(int i=0;i<n;i++){
            for(int j=0;j<i;j++){
                //第i个信封的长度和宽度都大于第j个信封,则可以把它嵌套到i里面信封里面
                if(envelopes[j][0]<envelopes[i][0]&&envelopes[j][1]<envelopes[i][1]){
                    f[i]=Math.max(f[i],f[j]+1);
                }
            }
            res=Math.max(res,f[i]);
        }
        return res;
    }
}

思路2:

  • 1.envelopes的长度进行从小到大排序,在长度相等的情况下,对它的宽度也进行从大到小排序得到 [1,1],[2,3],[4,6],[4,5],[4,7],最后问题转换成求 1,3,6,5,7中的最大递增子序列
  • 2.定义一个ends数组。ends[i]代表遍历到目前为止,所有长度为i+1的递增子序列的最小结尾数。初始化ends[0] = 1;即1 ,3,6,5,7遍历到1的时候,长度为1的递增子序列的最小结尾数就是1。
  • 一次更新ends数组,for循环遍历1,3,6,5,7,从i=1遍历到尾部。在ends里面进行二分查找,查找此刻dots[i]在ends的哪个部分,如果是没更新到的地方则更新此时的ends[i];如下:
for (int i = 1; i < dots.length; i++) {
       l = 0;
       r = right;
       //当要算以dots[i]结尾的最长递增子序列时,
       //二分查找去ends里面更新长度为i的最小结尾数
       while (l <= r) {
           mid = (l + r) / 2;
           if (dots[i].h > ends[mid]) {
               l = mid + 1;
           } else {
               r = mid - 1;
           }
       }
       right = Math.max(right, l);
       //更新最小结尾数
       ends[l] = dots[i].h;
   }

第一次遍历: i = 1; l = r =mid = 0; 
      dot[1].h = 3 > ends[0] = 1
      l = 1
      right = 1;ends[1] = 3;
第二次遍历: i = 2; l=0,r = 1,mid = 0;
      dot[2].h = 6 > ends[0] = 1
      l = 1
      mid = 1,l =2
      right = 2,end[2] = 6;
  …

import java.util.Arrays;
import java.util.Comparator;

/**
 * Created by qiulig on 2019/7/29
 */
public class Main_354 {
    //执行用时 :33 ms, 在所有 Java 提交中击败了87.11%的用户
    //内存消耗 :47.1 MB, 在所有 Java 提交中击败了80.68%的用户
    public static void main(String[] args) {
        int[][] envelopes=new int[][]{{4,5},{4,6},{6,7},{2,3},{1,1}};
        System.out.print(maxEnvelopes(envelopes));
    }
        public static class Dot {
            public int w;
            public int h;

            public Dot(int weight, int hight) {
                w = weight;
                h = hight;
            }
        }
        //长度从小到大排序,在长度相等的情况下,高度从大到小排序
        public static class DotComparator implements Comparator<Dot> {
            @Override
            public int compare(Dot o1, Dot o2) {
                if (o1.w != o2.w) {
                    return o1.w - o2.w;
                } else {
                    return o2.h - o1.h;
                }
            }
        }
        public static int maxEnvelopes(int[][] es) {
            if (es == null || es.length == 0 || es[0] == null || es[0].length != 2) {
                return 0;
            }
            Dot[] dots = new Dot[es.length];
            for (int i = 0; i < es.length; i++) {
                dots[i] = new Dot(es[i][0], es[i][1]);
            }
            //排序之后长度w: 1  2  4  4  6
            //    对应高度h: 1  3  6  5  7
            //最后问题转换成求 1  3  6  5  7中的最大递增子序列
            Arrays.sort(dots, new DotComparator());
            //ends[i]代表遍历到目前为止,所有长度为i+1的递增子序列的最小结尾数
            int[] ends = new int[es.length];
            //代表第一个信封的长度为1的最小结尾数是dots[0].h
            ends[0] = dots[0].h;

            int right = 0;
            int l = 0;
            int r = 0;
            int mid = 0;
            for (int i = 1; i < dots.length; i++) {
                l = 0;
                r = right;
                //当要算以dots[i]结尾的最长递增子序列时,
                //二分查找去ends里面更新长度为i的最小结尾数
                while (l <= r) {
                    mid = (l + r) / 2;
                    if (dots[i].h > ends[mid]) {
                        l = mid + 1;
                    } else {
                        r = mid - 1;
                    }
                }
                right = Math.max(right, l);
                //更新最小结尾数
                ends[l] = dots[i].h;
            }
            return right + 1;
        }
    }

如果信封可旋转,则我们可以把输入稍微定死,输入[w,h]定死了大的数的代表的是信封的长,小的数代表的是信封的宽,即如果输入w<h,则把他们互换一下位置。

搭积木

https://www.nowcoder.com/questionTerminal/55371b74b2f243e3820e57ee4c7b5504

 小明有一袋子长方形的积木,如果一个积木A的长和宽都不大于另外一个积木B的长和宽,则积木A可以搭在积木B的上面。好奇的小明特别想知道这一袋子积木最多可以搭多少层,你能帮他想想办法吗? 定义每一个长方形的长L和宽W都为正整数,并且1 <= W <= L <= INT_MAX, 袋子里面长方形的个数为N, 并且 1 <= N <= 1000000. 假如袋子里共有5个积木分别为 (2, 2), (2, 4), (3, 3), (2, 5), (4, 5), 则不难判断这些积木最多可以搭成4层, 因为(2, 2) < (2, 4) < (2, 5) < (4, 5)。

  • 同如上思路2,先将宽度进行排序,将问题转化成求长度的最大递增子序列。

package 拼多多;
import java.util.*;
public class Main_3{
    static class Rectangle{
        private int Weight; //宽度
        private int Len;  //长度
        public Rectangle(int x, int y){
            Weight = x;
            Len = y;
        }
    }
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        ArrayList<Rectangle> list = new ArrayList<>();
        for(int i = 0; i < n; i++){
            int x = sc.nextInt();
            int y = sc.nextInt();
            Rectangle r = new Rectangle(x, y);
            list.add(r);
        }
        //排序,宽度从小到大,在宽度一样的情况下,长度从小到大
        Collections.sort(list, new Comparator<Rectangle>(){
            @Override
            public int compare(Rectangle o1, Rectangle o2) {
                if(o1.Weight == o2.Weight)
                    return o1.Len - o2.Len;
                else
                    return o1.Weight -o2.Weight;
            }
        });
        //最后问题转换成求长度的最大递增子序列
        //用于代表遍历到目前为止,所有长度为i+1的递增子序列的最小结尾数
        int[] arr = new int[n];
        int max = 1;
        arr[0] = list.get(0).Len;
        for(int i = 1; i < n; i++){
            if(list.get(i).Len >= arr[max-1]){
                arr[max++] = list.get(i).Len;
            }else{
                int left = 0;
                int right = max -1;
                int L = list.get(i).Len;
                while(left < right){
                    int mid = (left + right) >> 1;
                    if(arr[mid] == L){
                        left = mid;
                        break;
                    }else if(arr[mid] > L){
                        right--;
                    }else
                        left++;
                }
                arr[left] = L;
            }
        }
        System.out.println(max);
    }
}

在这里插入图片描述

拼多多学霸批的堆积木

已知N个积木的长度,当堆积木的时候,要求每层积木的长度严格比其下方的积木小,而且每块积木只能承受自身重量的7倍,问可以堆多高的积木
给了个测试用例
输入:
10
1 2 3 4 5 6 7 8 9 10
1 1 1 1 1 1 1 1 1 10
输出:9

  • 个人思路觉得这题还是上面堆积木,俄罗斯套信封题一样,只是积木的长度 = 俄罗斯信封的长度,积木的重量*7 = 俄罗斯信封的高度。应该是这样子叭叭叭~~~~~~~~~~~~~~~~~~小菜鸟不知了。。。。。。。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值