【leetCode】 - 救生艇

182 阅读2分钟

这是我参与8月更文挑战的第27天,活动详情查看:8月更文挑战

题目

881. 救生艇

解析

既然是要多少条船,那么最朴素的方式就是:

  • 一个一个往里面塞,塞满了我们从头开始,并标记我们选了哪些人

基于这个朴素的想法,我们构建如下的解答:

     public static int numRescueBoats(int[] people, int limit) {
         boolean[] select = new boolean[people.length];
         int use = 0;
         for (int i = 0; i < people.length; i++) {
             if(select[i]) continue;
             int total = people[i];
             select[i] = true;
             for (int j = i + 1; j < people.length; j++) {
                 if(select[j] || total + people[j] > limit) continue;
                 total+= people[j];
                 select[j] = true;
             }
             use++;
         }
         return use;
     }

然后我们直接在这个case上挂了:

 [21,40,16,24,30]
 50

我们多输出了一个,预期3我们输出4。

因为按照我们的分组,我们4个装载如下:

[21,16] [40] [24] [30]

但其实3个就够了:

[21,24] [40] [16,30]

也就是说我们的空间利用率不够。

为了避免这个情况,我们可以:

  • 将数组先排序,例如上面的,排序为:[16,21,24,30,40]

  • 随后我们从大端取一个作为开始,从小端取剩余的装船

    例如上面的例子:

    [40] [30,->16] [24,->21]

同时注意到题目中的限定条件: 每艘船最多可同时载两人,但条件是这些人的重量之和最多为 limit。

我们用代码描述下我们第二次的代码:

     public static int numRescueBoats2(int[] people, int limit){
         boolean[] select = new boolean[people.length];
         int use = 0;
         Arrays.sort(people);
         for (int i = people.length - 1; i >= 0; i--) {
             //因为我们是从右往左取,那么如果取到读过了的数,说明已经装进去了,那么再之前的肯定也已经装进去了,我们就直接返回了
             if(select[i]) break;
             int total = people[i];
             select[i] = true;
             //i往右的数我们都装了
             for (int i1 = 0; i1 < i; i1++) {
                 if(select[i1]) continue;
                 //我们已经排序过了,如果此时就大,那么后续也一样会大
                 if(total + people[i1] > limit) break;
                 select[i1] = true;
                 break;
             }
             use ++;
         }
         return use;
     }

结果:

执行用时:2085 ms, 在所有 Java 提交中击败了5.22%的用户

内存消耗:46 MB, 在所有 Java 提交中击败了99.47%的用户

虽然是执行通过了,但是这个成绩显然很慢,我们有什么方法可以快一点吗?

  • 其实我们知道:排过序之后,一旦右边的数确定了,实际上每次我们取数的时候有一个隐含的条件是:

    • 我们下一次取的数,带上的数要么是剩下数最小的要么就带不上

那么我们就把第二个循环去掉变成变量维护即可,同样地我们也不要这个布尔数组了:

 public static int numRescueBoats2(int[] people, int limit){
         int use = 0;
         Arrays.sort(people);
         int left = 0;
         for (int i = people.length - 1; i >= left; i--) { 
             if(people[left]+people[i]<=limit) {
                 left++;
             }
             use ++;
         }
         return use;
 }

执行用时:16 ms, 在所有 Java 提交中击败了95.52%的用户

内存消耗:47.4 MB, 在所有 Java 提交中击败了33.16%的用户

\