Java 算法-Russian Doll Envelopes(动态规划或者二分法)

  先来看一下题
  题意:

You have a number of envelopes with widths and heights given as
 a pair of integers (w, h). One envelope can fit into another if and 
only if both the width and height of one envelope is greater than 
the width and height of the other envelope.

What is the maximum number of envelopes can you Russian doll? 
(put one inside other)

  样例:

Given envelopes = [[5,4],[6,4],[6,7],[2,3]], 
the maximum number of envelopes you can Russian doll is 3 ([2,3]
 => [5,4] => [6,7]).

  这个题的大意是这样的:给定一系列有大小的信封,请问像俄罗斯娃娃装起来,最多可以装几个,相同宽或者高的信封不能装。

1.动态规划(超时)

  刚刚看到这个题时,想到的是动态规划,因为自己的动态比较薄弱,因此更加想要动态规划做了。于是乎,经过填表,找出了动态规划方程。先说一下思想:我们遍历信封,看看当前当前要装其他信封的信封能装下的最大个数,说得很抽象,看一张表:



  但是这里需要注意的是:这里信封的顺序是条件的,我们必须按照宽(也就是说,第一个长度)的升序排序,高升序和降序都行。因为我们必须先把小信封的最大值求出,后面大信封在装小信封时,取得的最大值才是正确的;否则,我们先按照小信封的最大值(没有求),求大信封的最大值,如果之后小信封的最大值改变,但是大信封的最大值没有更新。

public static int maxEnvelopes(int[][] envelopes) {
        if (envelopes == null || envelopes.length == 0)
            return 0;
        //排序,按照宽的升序和高的降序排序(如果宽相同的话,则按宽的高的降序排序)
        Arrays.sort(envelopes, (a, b) -> {
            if (a[0] != b[0]) {
                return a[0] - b[0];
            } else {
                return a[1] - b[1];
            }
        });
        int max = 1;
        int[] a = new int[envelopes.length];
        //这里将数组初始化为1,上图的表中是0,如果这里初始化为0话,最后max需要加1
        Arrays.fill(a, 1);
        for (int i = 0; i < envelopes.length; i++) {
            for (int j = 0; j < i; j++) {
                //当符合要求是在选择更新a表
                if (envelopes[j][0] < envelopes[i][0] && envelopes[j][1] < envelopes[i][1]) {
                    a[i] = Math.max(a[i], 1 + a[j]);
                    if (a[i] > max) {
                        max = a[i];
                    }
                }
            }

        }
        return max;
    }

2.二分法

  感觉动态已经能够解决了,其实不然,动态规划也超时了。具体在哪里超时的,我也不太知道,估计是在两重for循环那里超时了,后面在网上找到了二分法的解决办法

(1).前提

  二分法的排序必须按照宽的升序,和高的降序来排。至于为什么呢?后面再说。例如:原来的序列:[[5,4],[6,4],[6,7],[2,3],[6,8],[7,8],[1,3]],排序之后:[[1,3],[2,3],[5,4],[6,8],[6,7],[6,4],[7,8]]

(2).思路

  首先我们用一个List集合来保存每个加进来信封的高(第二个数),当要进来的那个信封的高比List集合的最后数据大的话,则将这个高加入List集合,这是因为经过我们之前的排序,如果一个信封的高大于前一个信封的高,必定这个信封的宽也大于前一个的宽。可能看不太懂,如图所示:



  但是如果进来的高比最后一个数据小或者等于呢?首先肯定不能加到集合中去,那为什么会替换(后面代码中会有替换的操作)。想一下,我们是按照宽的升序排序,后面加入来的信封肯定大于或者等于前面加进去的信封的宽;后面进来信封的高小于或者等于时,有两种情况:宽大于前一个信封的宽或者等于前面的一个宽。
   二分法来替换的目的是:List集合的数据是加进来信封的高的升序,同时宽也是升序的。当即将进来的信封高小于或者等于前一个的高,于是在List集合找到它的位置保存下来,相当于说,每次加入信封时,我们必须保证List集合的数据是按照升序排序的,这样才能保证更多的加入信封。
  而这里为什么要按照高的降序排序呢?这是为了简便当即将进来的高比前一个的高大的话,这里只有一种可能,如果按照升序排序的话,还要考虑到宽等于的情况。

public static int maxEnvelopes(int[][] envelopes) {
        if (envelopes == null || envelopes.length == 0)
            return 0;

        Arrays.sort(envelopes, new Comparator<int[]>() {
            public int compare(int[] a, int[] b) {
                if (a[0] != b[0]) {
                    return a[0] - b[0]; // ascending order
                } else {
                    return b[1] - a[1]; // descending order
                }
            }
        });
        ArrayList<Integer> list = new ArrayList<Integer>();
        //遍历每个信封,相当于决定每个信封的位置
        for (int i = 0; i < envelopes.length; i++) {

            if (list.size() == 0 || list.get(list.size() - 1) < envelopes[i][1]) {
                list.add(envelopes[i][1]);
                continue;
            }
            //二分替换,使得加进来的每个信封的宽在List集合中是升序排序
            int l = 0;
            int r = list.size() - 1;
            while (l < r) {
                int m = (l + r) / 2;
                if (list.get(m) < envelopes[i][1]) {
                    l = m + 1;
                } else {
                    r = m;
                }
            }
            list.set(r, envelopes[i][1]);
        }

        return list.size();
    }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,460评论 25 707
  • java笔记第一天 == 和 equals ==比较的比较的是两个变量的值是否相等,对于引用型变量表示的是两个变量...
    jmychou阅读 1,485评论 0 3
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,598评论 18 139
  • 三尺讲台方寸地,教书育人立功绩。 传业授道师之本,桃李芬芳留清史。 吾辈岂能不敬尔,品格高尚真君子。 师恩殷殷无以...
    梦谷阅读 285评论 1 2
  • 早晨。 妈妈,我刚去游乐场溜哒了一圈,爸爸说周六会带我好好去玩一趟。 来,伸手…… 那天我可以带小晴晴一起去吗?她...
    一提阅读 164评论 0 4