6010. 完成旅途的最少时间,二分查找的好(ěxīn)题

6010. 完成旅途的最少时间

282场周赛中的第三题,比赛中没做出来,提交基本都是超时,最后半小时想出来要用二分查找但是很多莫名bug,下午看了别人的题解自己做还是很多bug,提交第16次终于通过了,因此记录一下遇到的坑。

需注意的点

1.二分查找开始时确定最大值和最小值

我的想法是:

假设每辆车都用最少的单趟时间,得到最小值:最小时间=旅途总数*最小的单趟时间/车的数量;同理,最大时间=旅途总数*最大的单趟时间/车的数量

这样做有一个很严重的问题:int型的除法只保留整数,会造成巨大的误差!

(例如:最大时间=199(次旅途)*2(最大单趟时间)/100(辆车)=3(分钟)。然而3分钟内100辆车是根本跑不完199趟的)。

虽然想到了这一点,但我采用的方法是尝试把int转换为double,这样改巨麻烦还容易错呜呜呜~

下午才想到办法,整数除法的误差只会使结果偏小,这样就只会对最大值造成重要影响,把可能出现的范围加上即可,这里有两种加法:

  • 最大时间=旅途总数/车的数量*最大的单趟时间 这样可能会还有一部分旅途数小于车数(即剩下的旅途数不需要所有的车都出动再跑一趟),这部分被舍去了,但其实还得出动部分车,即改为最大时间=(旅途总数/车的数量+1)*最大的单趟时间
  • 最大时间=旅途总数*最大的单趟时间/车的数量 这样可能会还有一部分时间的数值小于车数量的值(即剩下的时间还不够所有车一起再多走1分钟),被舍去了。但实际上剩下的时间还是需要完成的,即还是需要派出部分车,而派出的车一旦出动,就必须完整走完一趟。因此原式子改为最大时间=旅途总数*最大的单趟时间/车的数量+最大的单趟时间

如果要用带整数除法的解法就一定要理解误差发生的含义,然后再想怎么弥补,切忌从数学的角度出发,认为整数除法后被舍去的是小数,而小数一定小于1,于是直接在任意除法结束后直接+1。例如上面的情况2,如果改成最大时间=旅途总数*最大的单趟时间/车的数量+1,好像是把舍去的小数部分都超额补回来了,但是从含义上理解,有可能所有的车都出动,但又都只走一分钟吗?今天在这个小地方卡了很久,说白了是自己懒得思考的锅。

如果想不通,就尽量避开整数除法,看了别人的题解,采取的是:最小时间=最小的单趟时间数;假设只有一辆车,则最大时间=旅途总数*最小的单趟时间。这样确实可以确定范围,并且避开整型除法(但只用乘法会超出int范围,必须转换为long long)。

既然只是确定范围,就不用在一种想法上死磕,卡住了就想想有没有更好理解、更不会产生误差的方法。

2.查找过程中max和min的值变化

常规二分查找是当取mid时,如果结果>目标时使max=mid-1;结果<目标时,min=mid+1,结果==目标时返回mid。

但是这道题有点不同:

  • 取mid时,如果结果>目标,可能是因为这一分钟内到达目的地的车数太多,导致结果从上一分钟的小于目标变成这一分钟的超出目标。也就是说,虽然此时结果大于目标,但是mid可能就是要找的答案,因此,本题中计算结果>目标时,令max=mid。
  • 如果结果==目标,可能是因为这一分钟内没有车到达目的地,也许之前的某一分钟才是最终答案。因此,本题中不能在计算结果==目标时return mid,要继续查找,知道最终范围内只剩一个值。

3.结束查找的出口

循环出口纠结了好一会儿是min<max还是min<=max,下次遇到记得这样考虑:

  1. 可以用2~4个来模拟一下,看最后会不会出现min>max情况,本题就不会出现这种情况,如果while(min<=max),将进入无限循环。
  2. 考虑如果用min<max,会不会有元素被忽略,例如如果返回的是bool型,且只在循环内查找,就必须用min<=max,或者单独判断只有一个元素的情况,本题不太可能只有一个元素,但就算只有一个元素,返回的也是这个元素(而非bool类型),所以可以不需要=。

其它一些细节

1.可以用max_element(s.begin(),s.end) ,min_element(s.begin(),s.end) 返回最大最小值对应的迭代器(带*调用哦),这样比起sort(),再取头尾,有明显的效率提升。

auto most =max_element(time.begin(), time.end());
auto least =min_element(time.begin(), time.end());

代码

class Solution {
public:
    long long minimumTime(vector<int>& time, int totalTrips) {
        long long total;
        auto most =max_element(time.begin(), time.end());
        auto least =min_element(time.begin(), time.end());
        long long max=(long long)((long long)totalTrips* (*most)/time.size()+time[time.size()-1]);
        long long min=(long long)(totalTrips/time.size())*(*least);
        long long mid=(min+max)/2; 
        while(min<max){
            total=0;
            for(int j:time){
                total+=mid/j;  
            }
            if(total>=totalTrips){
                max=mid;
            }
            else if(total<totalTrips){
                min=mid+1;
            }
            mid=(min+max)/2;
        }return mid;
    }
    
};

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值