加载中...
881-救生艇(Boats to Save People)
发表于:2021-12-03 | 分类: 中等
字数统计: 1.5k | 阅读时长: 6分钟 | 阅读量:

原文链接: https://leetcode-cn.com/problems/boats-to-save-people

英文原文

You are given an array people where people[i] is the weight of the ith person, and an infinite number of boats where each boat can carry a maximum weight of limit. Each boat carries at most two people at the same time, provided the sum of the weight of those people is at most limit.

Return the minimum number of boats to carry every given person.

 

Example 1:

Input: people = [1,2], limit = 3
Output: 1
Explanation: 1 boat (1, 2)

Example 2:

Input: people = [3,2,2,1], limit = 3
Output: 3
Explanation: 3 boats (1, 2), (2) and (3)

Example 3:

Input: people = [3,5,3,4], limit = 5
Output: 4
Explanation: 4 boats (3), (3), (4), (5)

 

Constraints:

  • 1 <= people.length <= 5 * 104
  • 1 <= people[i] <= limit <= 3 * 104

中文题目

第 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

通过代码

高赞题解

贪心

一个直观的想法是:由于一个船要么载两人要么载一人,在人数给定的情况下,为了让使用的总船数最小,要当尽可能让更多船载两人,即尽可能多的构造出数量之和不超过 $limit$ 的二元组。

先对 $people$ 进行排序,然后使用两个指针 lr 分别从首尾开始进行匹配:

  • 如果 $people[l] + people[r] <= limit$,说明两者可以同船,此时船的数量加一,两个指针分别往中间靠拢;
  • 如果 $people[l] + people[r] > limit$,说明不能成组,由于题目确保人的重量不会超过 $limit$,此时让 $people[r]$ 独立成船,船的数量加一,r 指针左移。

我们猜想这样「最重匹配最轻、次重匹配次轻」的做法能使双人船的数量最大化。

接下来,我们使用「归纳法」证明猜想的正确性。

假设最优成船组合中二元组的数量为 $c1$,我们贪心做法的二元组数量为 $c2$。

最终答案 = 符合条件的二元组的数量 + 剩余人数数量,而在符合条件的二元组数量固定的情况下,剩余人数也固定。因此我们只需要证明 $c1 = c2$ 即可。

通常使用「归纳法」进行证明,都会先从边界入手。

当我们处理最重的人 $people[r]$(此时 $r$ 为原始右边界 $n - 1$)时:

  • 假设其与 $people[l]$(此时 $l$ 为原始左边界 $0$)之和超过 $limit$,说明 $people[r]$ 与数组任一成员组合都会超过 $limit$,即无论在最优组合还是贪心组合中,$people[r]$ 都会独立成船;
  • 假设 $people[r] + people[l] <= limit$,说明数组中存在至少一个成员能够与 $people[l]$ 成船:
    • 假设在最优组合中 $people[l]$ 独立成船,此时如果将贪心组合 $(people[l], people[r])$ 中的 $people[l]$ 拆分出来独立成船,贪心二元组数量 $c2$ 必然不会变大(可能还会变差),即将「贪心解」调整成「最优解」结果不会变好;
    • 假设在最优组合中,$people[l]$ 不是独立成船,又因此当前 $r$ 处于原始右边界,因此与 $people[l]$ 成组的成员 $people[x]$ 必然满足 $people[x] <= people[r]$。
      此时我们将 $people[x]$ 和 $people[r]$ 位置进行交换(将贪心组合调整成最优组合),此时带来的影响包括:
      • 与 $people[l]$ 成组的对象从 $people[r]$ 变为 $people[x]$,但因为 $people[x] <= people[r]$,即有 $people[l] + people[x] <= people[l] + people[r] <= limit$,仍为合法二元组,消耗船的数量为 $1$;
      • 原本位置 $x$ 的值从 $people[x]$ 变大为 $people[r]$,如果调整后的值能组成二元组,那么原本更小的值也能组成二元组,结果没有变化;如果调整后不能成为组成二元组,那么结果可能会因此变差。
      综上,将 $people[x]$ 和 $people[r]$ 位置进行交换(将贪心组合调整成最优组合),贪心二元组数量 $c2$ 不会变大,即将「贪心解」调整成「最优解」结果不会变好。

对于边界情况,我们证明了从「贪心解」调整为「最优解」不会使得结果更好,因此可以保留当前的贪心决策,然后将问题规模缩减为 $n - 1$ 或者 $n - 2$,同时数列仍然满足升序特性,即归纳分析所依赖的结构没有发生改变,可以将上述的推理分析推广到每一个决策的回合(新边界)中。

至此,我们证明了将「贪心解」调整为「最优解」结果不会变好,即贪心解是最优解之一。

代码:

[]
class Solution { public int numRescueBoats(int[] people, int limit) { Arrays.sort(people); int n = people.length; int l = 0, r = n - 1; int ans = 0; while (l <= r) { if (people[l] + people[r] <= limit) l++; r--; ans++; } return ans; } }
  • 时间复杂度:排序复杂度为 $O(n\log{n})$;双指针统计答案复杂度为 $O(n)$。整体复杂度为 $O(n\log{n})$
  • 空间复杂度:$O(\log{n})$

最后

如果有帮助到你,请给题解点个赞和收藏,让更多的人看到 ~ (“▔□▔)/

也欢迎你 关注我(公主号后台回复「送书」即可参与看题解学算法送实体书长期活动)或 加入「组队打卡」小群 ,提供写「证明」&「思路」的高质量题解。

所有题解已经加入 刷题指南,欢迎 star 哦 ~

统计信息

通过次数 提交次数 AC比率
48983 91018 53.8%

提交历史

提交时间 提交结果 执行时间 内存消耗 语言
上一篇:
882-细分图中的可到达结点(Reachable Nodes In Subdivided Graph)
下一篇:
884-两句话中的不常见单词(Uncommon Words from Two Sentences)
本文目录
本文目录