文章目录
题目
标题和出处
标题:救生艇
出处:881. 救生艇
难度
5 级
题目描述
要求
给定数组 people \texttt{people} people,其中 peoplei \texttt{peoplei} peoplei 表示第 i \texttt{i} i 个人的体重。船的数量不限,每艘船可以承载的最大重量为 limit \texttt{limit} limit,每艘船最多可同时承载两人。
返回承载所有人所需的最小船数。
示例
示例 1:
输入: people = 1,2, limit = 3 \texttt{people = 1,2, limit = 3} people = 1,2, limit = 3
输出: 1 \texttt{1} 1
解释: 1 \texttt{1} 1 艘船载 (1, 2) \texttt{(1, 2)} (1, 2)。
示例 2:
输入: people = 3,2,2,1, limit = 3 \texttt{people = 3,2,2,1, limit = 3} people = 3,2,2,1, limit = 3
输出: 3 \texttt{3} 3
解释: 3 \texttt{3} 3 艘船分别载 (1, 2) \texttt{(1, 2)} (1, 2)、 (2) \texttt{(2)} (2) 和 (3) \texttt{(3)} (3)。
示例 3:
输入: people = 3,5,3,4, limit = 5 \texttt{people = 3,5,3,4, limit = 5} people = 3,5,3,4, limit = 5
输出: 4 \texttt{4} 4
解释: 4 \texttt{4} 4 艘船分别载 (3) \texttt{(3)} (3)、 (3) \texttt{(3)} (3)、 (4) \texttt{(4)} (4) 和 (5) \texttt{(5)} (5)。
数据范围
- 1 ≤ people.length ≤ 5 × 10 4 \texttt{1} \le \texttt{people.length} \le \texttt{5} \times \texttt{10}^\texttt{4} 1≤people.length≤5×104
- 1 ≤ peoplei ≤ limit ≤ 3 × 10 4 \texttt{1} \le \texttt{peoplei} \le \texttt{limit} \le \texttt{3} \times \texttt{10}^\texttt{4} 1≤peoplei≤limit≤3×104
解法
思路和算法
为了使承载所有人的船数最小,应使承载两人的船数最大化,优先考虑使用一艘船承载两人。
对于体重最小的人,应考虑将其与体重最大的人安排同一艘船,如果两人体重之和不超过 limit \textit{limit} limit 则安排同一艘船,否则将体重最大的人单独安排一艘船。理由如下。
-
如果两人体重之和不超过 limit \textit{limit} limit,则体重最小的人可以与任何人乘坐同一艘船。将体重最小的人与体重最大的人安排同一艘船,可以用一艘船承载两人。如果将体重最小的人与体重不是最大的人安排同一艘船,则剩余的人当中不一定存在可以和体重最大的人安排同一艘船的人,因此不可能使用更少的船承载所有人,相反可能需要更多的船承载所有人。
-
如果两人体重之和超过 limit \textit{limit} limit,则体重最大的人与其他任何一个人的体重之和都超过 limit \textit{limit} limit,因此无法将体重最大的人与其他任何人安排同一艘船,必须将体重最大的人单独安排一艘船。
上述策略为贪心策略。另一种证明贪心策略可以得到最小船数的方法如下。
根据上述策略,假设有四个人分别安排到两艘船上,四个人的体重分别是 w 1 w_1 w1、 w 2 w_2 w2、 w 3 w_3 w3 和 w 4 w_4 w4,满足 w 1 ≤ w 2 ≤ w 3 ≤ w 4 w_1 \le w_2 \le w_3 \le w_4 w1≤w2≤w3≤w4,则体重 w 1 w_1 w1 和 w 4 w_4 w4 的两个人安排同一艘船,体重 w 2 w_2 w2 和 w 3 w_3 w3 的两个人安排同一艘船, w 1 + w 4 ≤ limit w_1 + w_4 \le \textit{limit} w1+w4≤limit, w 2 + w 3 ≤ limit w_2 + w_3 \le \textit{limit} w2+w3≤limit。如果不将体重 w 1 w_1 w1 的人和体重 w 4 w_4 w4 的人安排同一艘船,则承载这四个人的船数非但不可能减少,反而可能增加,理由如下。
如果将体重 w 1 w_1 w1 的人和体重 w 2 w_2 w2 或 w 3 w_3 w3 的人安排同一艘船,则剩余两个人的体重之和一定大于等于 w 1 + w 4 w_1 + w_4 w1+w4。如果剩余两个人的体重之和小于等于 limit \textit{limit} limit 则可以将剩余两个人安排同一艘船,此时需要两艘船承载这四个人;如果剩余两个人的体重之和大于 limit \textit{limit} limit 则不能将剩余两个人安排同一艘船,此时需要大于两艘船承载这四个人。
实现方面,可以首先将数组 people \textit{people} people 按升序排序,然后使用双指针从两端向中间遍历数组,计算最小船数。用 left \textit{left} left 和 right \textit{right} right 表示双指针,初始时 left \textit{left} left 指向 people \textit{people} people 的首个元素, right \textit{right} right 指向 people \textit{people} people 的末尾元素,则初始时 left \textit{left} left 和 right \textit{right} right 分别指向 people \textit{people} people 的最小元素和最大元素。当 left ≤ right \textit{left} \le \textit{right} left≤right 时,执行如下操作。
-
如果 people left + people right ≤ limit \textit{people}\\textit{left} + \textit{people}\\textit{right} \le \textit{limit} peopleleft+peopleright≤limit,则可以将体重为 people left \textit{people}\\textit{left} peopleleft 和 people right \textit{people}\\textit{right} peopleright 的两个人安排同一艘船, left \textit{left} left 向右移动一位, right \textit{right} right 向左移动一位;否则,将体重为 people right \textit{people}\\textit{right} peopleright 的人单独安排一艘船, left \textit{left} left 保持不变, right \textit{right} right 向左移动一位。
-
将船数加 1 1 1。
遍历结束之后,即可得到最小船数。
代码
java
class Solution {
public int numRescueBoats(int[] people, int limit) {
int boats = 0;
Arrays.sort(people);
int left = 0, right = people.length - 1;
while (left <= right) {
if (people[left] + people[right] <= limit) {
left++;
}
right--;
boats++;
}
return boats;
}
}
复杂度分析
-
时间复杂度: O ( n log n ) O(n \log n) O(nlogn),其中 n n n 是数组 people \textit{people} people 的长度。将数组 people \textit{people} people 排序需要 O ( n log n ) O(n \log n) O(nlogn) 的时间,排序之后使用双指针遍历数组需要 O ( n ) O(n) O(n) 的时间,因此时间复杂度是 O ( n log n ) O(n \log n) O(nlogn)。
-
空间复杂度: O ( log n ) O(\log n) O(logn),其中 n n n 是数组 people \textit{people} people 的长度。排序需要 O ( log n ) O(\log n) O(logn) 的递归调用栈空间。