Medium
题目描述
给定数组 people,其中 people[i] 是第 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 <= 5 * 10^41 <= people[i] <= limit <= 3 * 10^4
解题思路
这是一个经典的贪心算法问题。关键思路是通过双指针技术最大化每艘船的利用率。
核心思想: 首先对人员体重进行排序,然后使用双指针分别指向最轻和最重的人。由于每艘船最多载两人,我们有以下策略:
- 如果最轻的人和最重的人的体重之和不超过限制,说明可以同时载两人,两个指针都移动
- 如果超过限制,说明最重的人必须单独乘船,只移动右指针
算法流程:
- 对 people 数组排序
- 设置左指针 left=0,右指针 right=n-1
- 统计船只数量 boats=0
- 当 left <= right 时:
- 如果 people[left] + people[right] <= limit,left++(最轻的人也上船)
- right–(最重的人上船)
- boats++
这种贪心策略是正确的,因为如果最重的人无法与最轻的人同船,那么他也无法与任何其他人同船,只能独自乘船。
代码实现
class Solution {
public:
int numRescueBoats(vector<int>& people, int limit) {
sort(people.begin(), people.end());
int left = 0, right = people.size() - 1;
int boats = 0;
while (left <= right) {
if (people[left] + people[right] <= limit) {
left++;
}
right--;
boats++;
}
return boats;
}
};
class Solution:
def numRescueBoats(self, people: List[int], limit: int) -> int:
people.sort()
left, right = 0, len(people) - 1
boats = 0
while left <= right:
if people[left] + people[right] <= limit:
left += 1
right -= 1
boats += 1
return boats
public class Solution {
public int NumRescueBoats(int[] people, int limit) {
Array.Sort(people);
int left = 0, right = people.Length - 1;
int boats = 0;
while (left <= right) {
if (people[left] + people[right] <= limit) {
left++;
}
right--;
boats++;
}
return boats;
}
}
var numRescueBoats = function(people, limit) {
people.sort((a, b) => a - b);
let left = 0, right = people.length - 1;
let boats = 0;
while (left <= right) {
if (people[left] + people[right] <= limit) {
left++;
}
right--;
boats++;
}
return boats;
};
复杂度分析
| 复杂度类型 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(n log n) | 主要开销在排序,双指针遍历为 O(n) |
| 空间复杂度 | O(1) | 只使用常量额外空间(不考虑排序的空间开销) |