题目:给定数组表示人的重量,现在用船去救这些人,每条小船载重限制为limit,最多坐两个人,求最少需要多少条小船?
分析:
(1)假如一开始就知道了这道题是使用贪心策略,其实还比较简单。最大最小的匹配尝试并证明一下就行了。但是一开始,不太容易往贪心上考虑。
(2)先来看看暴力破解,我们可以尝试将所有的people两人一组组队,这样一共会有大约(n-1)(n+1)/4中组队的情况,这里排列组合就不详细分奇偶讨论了。组队完成后,将不满足要求的组合拆开,看看最后哪种组合剩下的队伍最少。这样组队其实复杂度相当高。
(3)我们看看这个过程里都有哪些多余的步骤可以简化算法:
- 第一点,对于那些大于limit/2的people,彼此之间是无法组队的
- 基于第一点,我们考虑到较大的数字选择其实比较少,对于它们我们可以先考虑。而对于那些不大于limit/2的数字,任意组合都没有问题。
- 现在为一个最大的不超过limit的元素挑选队友,想到这里,贪心策略就不是很突兀了,我想试试会不会那些不超过limit的最大的数字,和当前未匹配的最小的数字组合就是答案呢?
证明:假设max是小于limit的最大数字,min是所有元素中最小的数字,如果
则它们搭配是最优的。
反证法:假设存在更优的情况,max,和min没有搭配在一起。
- 首先在这个最优解中,这两个人不能落单,否则将它们组合在一起,情况不会变差。
- 假设最有组合中,max与x组合,min与y组合;那么有
而 是已知的。所以
也成立。
到这里,假如我们将最优组合中的(max,x)(min,y)换成(max,min)(x,y)同样是一个最优解。
所以最大最小的搭配能够产生最优解。其实,只要让那些能匹配的大值元素不落单,都是最优解。
时间复杂度:
最大最小搭配需要排序,排序后两个指针双向遍历即可。
复杂度:
代码:
public int numRescueBoats(int[] people, int limit) {
int rs = people.length;
Arrays.sort(people);
int pre = 0,latter = people.length-1;
while(pre<latter){
if(people[latter--]+people[pre]<=limit){
pre++;
rs--;
}
}
return rs;
}