二分法

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

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


题目描述:上题中讲了一个故事,故事大意不用过多关注,有用部分为:某地主有一个大粮仓,这个粮仓容量为 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/。

原文地址:https://www.cnblogs.com/detrol/p/7533574.html