二分答案小结
by QTY_YTQ
我们一般是根据条件求解问题答案,有时候我们却可以枚举答案并判断是否可行或是否最优来得到最终的真实答案,我们此时可以使用二分枚举,通过对半切割,不断缩小最终答案所在的区间,最后找到答案,这样可以让枚举时间效率变为log(n),使时间效率得到提升。当我们遇到有诸如“使最大值最小”这样要求的问题时,可以考虑二分答案求解,下面是一些二分答案的一点总结。
二分答案比较合适的写法:
有时候会这样写:
while l<r do begin mid:=(l+r) div 2; if cheak(mid)=true then r:=mid-1 else l:=mid; end;
然而这样写适合于求最大值最小这样的情况,如果求最小值最大,当l+1=r且cheak(mid)=false时,l会被赋值为mid,而mid=(l+r) div 2=l,会一直循环的。
最好这样写:
while l<=r do begin mid:=(l+r) div 2; if cheak(mid)=true then begin ans:=mid; r:=mid-1; end else l:=mid+1; end;
收入计划(income.???)
【问题描述】
高考结束后,同学们大都找到了一份临时工作,渴望挣得一些零用钱。从今天起,Matrix67将连续工作N天(1<=N<=100000)。每一天末他可以领取当天及前面若干天里没有领取的工资,但他总共只有M(1<=M<=N)次领取工资的机会。Matrix67已经知道了在接下来的这N天里每一天他可以赚多少钱。为了避免自己滥用零花钱,他希望知道如何安排领取工资的时间才能使得领到工资最多的那一次工资数额最小。
注意:Matrix67必须恰好领工资M次,且需要将所有的工资全部领走(即最后一天末需要领一次工资)。
【输入数据】
第一行输入两个用空格隔开的正整数N和M。
以下N行每行一个不超过10000正整数,依次表示每一天的薪水。
【输出数据】
输出领取到的工资的最大值最小是多少。
【输入样例】
7 5
100
400
300
100
500
101
400
【输出样例】
500
【样例说明】
采取下面的方案可以使每次领到的工资不会多于500。这个答案不能再少了。
100 400 300 100 500 101 400 每一天的薪水
<------1 <-------2 <---3 <---4 <---5 领取工资的次数
500 400 500 101 400 领取到的工资
分析:
要求我们使领到的工资最大值最小,自然会想到二分,我们可以二分领到工资最最大值,并利用贪心的思想进行判断,每次从左向右扫描,对于每一天,如果这一天工资加之前没领取的工资小于等于最大值,那么可以在与之前的工资一起领取;如果超过了,就必须在分开来在下一次领取,也就是让每一次领取的工资在不超过最大值前提下尽可能大,这样可以得到至少需要另几次工资,若最少领取工资次数大于m,说明不可行,真实答案会大于这个工资最大值,改变下界,继续二分,否则改变上界,继续二分,直到找到答案。
代码:
program income; var a:array[0..100000]of longint; n,i,m,s,t,l,r,mid:longint; flag:boolean; begin assign(input,'income.in'); reset(input); assign(output,'income.out'); rewrite(output); readln(n,m); for i:=1 to n do begin read(a[i]);r:=r+a[i]; if a[i]>l then l:=a[i]; end; while l<r do begin mid:=(l+r) div 2; s:=0; t:=0; for i:=1 to n do begin if s+a[i]>mid then begin s:=0; t:=t+1; end; if s+a[i]<=mid then s:=s+a[i]; end; t:=t+1; if t>m then flag:=false else flag:=true; if flag=true then r:=mid else l:=mid+1; end; writeln(r); close(input); close(output); end.