881. 救生艇
第 i 个人的体重为 people[i],每艘船可以承载的最大重量为 limit。
每艘船最多可同时载两人,但条件是这些人的重量之和最多为 limit。
返回载到每一个人所需的最小船数。(保证每个人都能被船载)。
示例 1:
输入:people = [1,2], limit = 3
输出:1
解释:1 艘船载 (1, 2)
示例 2:
输入:people = [3,2,2,1], limit = 3
输出:3
解释:3 艘船分别载 (1, 2), (2) 和 (3)
示例 3:
输入:people = [3,5,3,4], limit = 5
输出:4
解释:4 艘船分别载 (3), (3), (4), (5)
提示:
1 <= people.length <= 50000
1 <= people[i] <= limit <= 30000
题解:
本题核心思想就是贪心
,只要把握好怎么去贪心
,便可以很容易的解决本题。
首先在逻辑层面思考:
- 由题可知,船有最大限重,且船最多可以坐两个人。那么先从
局部贪心
的角度思考,一艘船若能坐两个人绝不会让他坐一个人;再从全局贪心
的角度去思考,为了让船的数量尽可能的少,能坐在一艘船上的两个人一定是重量和趋于最大限重的,即我们尽可能的去让体重较大的和体重较小的坐在一起,这样的话可以充分提高船只的利用率。
在代码层面如何实现呢?
- 我们可以先对
people数组
从小到大进行排序,接着利用双指针
的思想,头尾相接
坐船即可,当然能够一起坐船自然是通过if
判断其总和小于limit
;一旦总和大于limit
时,根据实际情况可知,此时只能让体重较大的再小一点才能符合题意 (因为体重较小的在每次坐船时由于自身重量的优势,一定能够坐上船) ,因此双指针
中前者不动,后者向前移动; - 通过上述操作后,我们发现还有一些人没有坐上船,且可能会出现一艘船的
limit
刚好等于一个人的体重的情况,那么我们如何记录住这些人呢?这时我们可以采用逻辑删除
思想,对坐上船的人专门进行记录即可,详情可见代码注释。
代码:
class Solution {
public int numRescueBoats(int[] people, int limit) {
int res = 0; //需要船的数量
Arrays.sort(people); //排序
int[] isDelete = new int[people.length]; //逻辑删除数组
for(int i=0;i<isDelete.length;i++){
if(people[i]==limit){
//一旦人的体重刚好==limit,直接给他个船让他坐上走
res++; //需要的船+1
isDelete[i] = 1; //代表坐上船了
}
else{
isDelete[i] = 0; //代表还没坐上
}
}
int i=0,j=people.length-1;
while(i<j){
//一旦i,j碰面即双指针遍历结束了
if(isDelete[i]==1){
if(isDelete[j]==1){
//都坐上了,那么直接继续遍历
i++;
j--;
continue;
}
else{
//i坐上了,j没坐上,那么只让头部向后,尾部保持不动等待与他配对的‘体重较小的人’一起坐船
i++;
continue;
}
}
if(isDelete[i]==0){
if(isDelete[j]==1){
//思想同上
j--;
continue;
}
else{
//此为都没坐上的情况
if(people[i]+people[j] > limit){
//一旦选中的这俩人不能一起坐船,那么让体重较大的先去一边等着,去找体重稍微大的与体重较小的配对
j--;
continue;
}
else{
//可以一起坐船
res++;
isDelete[i] = 1;
isDelete[j] = 1;
i++;
j--;
continue;
}
}
}
}
//再遍历一次,找到还没坐上船的人,此时由于能配对的都配对上了,所以剩下的都是不能配对的,因此一人一条船即可
for(int n=0;n<isDelete.length;n++){
if(isDelete[n]==0){
res++;
isDelete[n] = 1;
}
}
return res;
}
}