zoukankan      html  css  js  c++  java
  • 二分法

    最近做了几道关于二分法的题目,觉得比较典型,因此拿出来说一说。

    首先,先把题目分享一下。


    题目描述:上题中讲了一个故事,故事大意不用过多关注,有用部分为:某地主有一个大粮仓,这个粮仓容量为 n 个单位,但这个粮仓有个小口,每天会有一部分麻雀过来偷吃粮食,同时地主每天也会从别的地方运来粮食填补。开始的时候粮仓是满的,麻雀偷吃和运量的具体信息如下:

    1⃣️从第一天开始,每天会有麻雀过来吃粮食,第 i 天会来 i 只麻雀,每只麻雀吃一个单位的粮食。

    2⃣️从第二天开始,地主每天会从别的地方运来 m 个单位的粮食加入到粮仓中,由于粮仓的总容量是 n ,因此运来的粮食填满粮仓后多余的粮食扔掉。

    题目要求,求出第几天粮仓第一次空了。

    针对这个过程举例如下

    上文意思是:假设粮仓容量 n 为 5 ,每天运粮数量为 2 ,那么

    开始之前,粮仓里有5 个单位的粮食,粮仓是满的。

    第一天 ,来了一只麻雀,吃了 1 单位的粮食,因此在第一天结束的时候,粮仓里剩余4单位的粮食。

    第二天,地主先运来 2 单位的粮食来填补粮仓,由于 n = 5 ,第一天剩余 4 ,现在运来  2 单位,所以 添进粮仓 1 单位后,有1 单位浪费掉。此时粮仓里有 5 单位的粮食(粮仓是满的)。填充完毕后,来了 2 只麻雀,吃掉 2 个单位的粮食,此时粮仓里剩余 3 个单位的粮食。

    第三天,地主又运来 2 单位的粮食,将只有 3 个单位粮食的粮仓再次填满,这次没有浪费,填充完毕,飞来 3 只麻雀,吃掉 3 单位的粮食,此时粮仓里剩余 2  个单位的粮食。

    第四天,地主运来 2 个单位的粮食,加入 原来剩余 2 单位粮食后,这时,粮仓里只有 4 单位的粮食,没有填满。填充完毕,飞来 4 只麻雀,吃掉 4 个单位的粮食,这时候粮仓空了。

    因此,答案即为 4 。

    题目的输入输出格式以及测试数据如下

    请务必认真仔细的记住题目中所给的数据范围。


    根据题目的介绍,可以发现,这道题首先可以分成两种情况,

    1⃣️ n < m 时,第一天 ,1 只麻雀吃过后剩 n - 1 粮食,第二天开始,运来 m 单位的粮食,将粮仓填满为 n ,第二天结束, 2 只麻雀吃过后剩 n - 2 粮食,在第三天开始时,运来的m粮食又将 粮仓填满为 n ,。 。 。直到 第 n - 2 的时候,n - 2 只麻雀吃过后,剩余 2 粮食,第 n - 1 天开始时,运来 m 单位粮食( m > n ),因此又将粮仓填满为 n ,飞来 n - 1 只麻雀,吃过后,粮仓 剩余 1 粮食,第 n 天开始,地主运来的 m 单位粮食又把粮仓填满了,这时飞来 n 只麻雀,正好吃完了 n 单位粮食,因此粮仓就空了 ,(题目要求一旦粮仓空了,就停止计算,不给地主接下来一天运来粮食的机会)由此可以看出,在这种情况中,粮仓吃空需要的天数就是粮仓的总容量 n 。

    2⃣️ n > m 时,在前 m 天,麻雀吃掉的粮食都能在第二天的时候补上,但到第 m + 1 天的时候,麻雀吃掉 m + 1粮食,在第 m + 2 的时候运来的 m 粮食 无法将 粮仓填满。也就是说,从第 m + 1 天开始,第 i 天粮仓的存粮数目在前一天的基础上减少 i 个单位,假设 在 第 m + k 天将粮食吃完了,那么从 第 m + 1 天开始计算,在 第 m +k - 1天结束的时候,就满足 

    (1+m) + (2+m) + (3+m) +。。。+(k-1+m)+ < (n-m) +(k-1)*m,

    在 第m + k 天结束的时候,就满足

    (1+m)+(2+ m)+。。。+(k+m)>=(n-m)+k*m。


    现在,可以将这个过程进行进一步化简:

    在m天之后,假设加完粮食后粮仓现在有粮食 x 个单位,天数是第 m + i 天,现在把整个粮仓分为两部分,一部分有粮食 m单位,另一部分有粮食 x-m 个单位,把飞来的 麻雀也分为两部分,一部分 有麻雀 m 只,另一部分 有麻雀 i 只,要求 m 只麻雀吃 m 个粮食的那部分,i 只麻雀去吃 x-m 的那部分粮食,那么m只麻雀 每天总能 和运来的 m 单位粮食相抵消,剩余的 x-m 那部分粮食会不断减少,直至吃光。也就意味着在 m+1到 m+i 这 i 天,每天 除去 m只剩余的麻雀 加起来吃掉的就是第 m 天 结束的时候 剩余的那部分粮食。m天结束的时候剩余的粮食为 n-m ,

    假设在第 m+k 天时候来的麻雀吃光了剩余的粮食。因此除去每天抵消的那部分,剩余的麻雀吃到粮食的总数为 n-m。有等式如下

    1+2+3+。。。+k >= n - m ,

    (这里用  >=  的原因是,在第 m +k 天的时候,不一定所有来的麻雀都刚好有粮食吃,因此麻雀数有可能比剩余粮食数多)

    并且,在 k-1天的时候,来的麻雀并没有将剩余的粮食吃完。满足

    1+2+3 。。。+(k-1)< n-m .

    之所以要将方法进一步精简的原因是,题目给的测试数据 是 long long ,第一种方法多加的 k 个m 增大了数据,是有可能导致爆掉 long long 的一个原因。反观第二种思路,数据减小了不少,会减小这个风险,但是本质上来讲是没有区别的,因为他们是属于同一量级的数据

    第一种思路 不等式左边为

    (1+m)+(2+ m)+。。。+(k+m)=k*m+(1+k)*k/2=k*m+k/2+k*k/2(包含k的二次方项)

    第二种思路不等式左边为

    1+2+3+。。。+k =(1+k)*k/2=k/2+k*k/2(同样包含k的二次方项)

    由于 天数 m 和 n 都是 long long 类型的,那么 k 也必然是 long long 类型的,如果数据给的是 long long 的上限,那么,二次方必然会超出上界。而且,做为一组优秀的测试数据,应该包含这样的边界数据来检测程序的稳定性。

    采用上面的判断条件来找 k ,只要数据足够大,闭着眼睛也能将 long long 爆掉,那是不是就意味着 这个方法行不通呢?并非如此,上面的思路是非常精简的一个思路了。那就在 k 的数据范围上做文章,k 的最大范围肯定是 大于1 而小于 n+m 的,假设 m 和 n 的数据为 1e18 量级,那么加和之后 数据为1e19 量级,由不等式 (1+k)*k/2=k/2+k*k/2=1+2+3+。。。+k >= n - m 可知,k*k 的数据为1e19 的量级的,则 k的量级最大界限变为 1e10。这样就稳了。

    既然选对了方向,接下来就是 找这个 k 值了,最简单的想法是加一层循环从第一天到第 n+m 天逐个判断是否满足条件,直到找到答案。o(n*n)的时间复杂度,在1e18这样量级的数据,超时是肯定的,因此找 k 的方法也需要改进。

    由上面的描述的可以发现在 m +k 天之前,麻雀没有将 剩余粮食吃完,而在 m+k 天之后,肯定能将粮食吃完,并且,越往后,吃不到粮食的麻雀越多。得出的规律为:越靠近 m+k 这一天,剩余的粮食或者吃不到粮食的麻雀会越少。当然,题目要求只考虑 m +k 天及之前的情况,不考虑 m+k 天之后,因此只需要考虑的特征就是 越靠近 m +k 这一天粮食剩余数越少这一特征。

    其次麻雀每天增长的数量有线性的关系,因此 如果 第 i 天麻雀没吃完,那么可以肯定 i 天之前,麻雀一定没有将粮食吃完,那就不用判断 i天之前的情况了。同样,如果 第 i 天 麻雀已经将粮食吃完了,那么在 i 天之后,麻雀一定能将粮食吃完,也不用考虑 i 天之后的情况。现在就需要每次找一个 测试的值 i ,将区间一分为二,在满足条件的那一半寻找。那么这个测试值取多少合适呢,经实践发现,每次将区间等分查找的效率最高,

    即 i 取 (左边界+右边界)/2。这就是著名的 二分思想。时间复杂度是o(log n)的。


    具体代码实现有兴趣的请打开网页 http://paste.ubuntu.com/24268046/。

  • 相关阅读:
    C++程序设计实验-3
    函数
    C++简单程序设计
    C++程序设计实验-2
    C++程序设计实验-1
    项目总结
    团队测试计划
    第二阶段团队绩效评分
    第二阶段scrum-10
    第二阶段scrum-9
  • 原文地址:https://www.cnblogs.com/detrol/p/7533574.html
Copyright © 2011-2022 走看看