k序列和
二分答案是参数搜索的一个改善。
是这样,对于一个问题,如果它的答案具有单调性质(即如果i不可行,那么大于i的解都不可行,而小于i的解有可能可行),进而用二分的方法枚举答案,再判断答案是否可行,直到求到符合条件为止。
例如:问题的答案范围是1到w之间的一个整数,求最小解,那么我们设s=1,t=w,之后mid=(s+t)整除2。然后判断当解是mid的时候这个问题能不能解决,如果能解决则和最优解比较,并且范围缩小到s到mid-1之间(因为即使这个范围没有解,那么mid是最小解);如果不能解决问题,则最小解肯定比mid要大,则范围缩小到mid+1和t之间。如此反复知道s=t时判断完结束。
这时候那个记录最优解的变量一定记录的是能够达到的最优解。
罗嗦了这么多,简单来说就是假设一个答案,然后判断是否可行,并且不断缩小范围。
二分枚举答案的时间复杂度是O(log2 n),而判断时间是O(K)的话总的时间复杂度是O(Klog2 n)如果某个问题符合这个性质并且K比较小的话这个方法相当实用。
举个例子:
k序列和
【问题描述】
对于一个给定的序列,将其分为k个部分,求各部分和的最大值的最小值。
【问题分析】
可采用二分答案的思想,设出一个答案ans,循环将和不超过ans的几个数分为一部分。直到最后若可以分为k部分则减小上界,反之增加下界。直到确定答案。
AYYZOJ 1588
1 var 2 n,k,i,p,l,r,m,s:longint; 3 a:array[1..100005] of longint; 4 begin 5 readln(n,k); 6 for i:=1 to n do 7 begin 8 read(a[i]); 9 inc(r,a[i]); 10 end; 11 while r-l>1 do 12 begin 13 m:=(l+r) shr 1; 14 s:=0; p:=0; 15 for i:=1 to n do 16 if s+a[i]<=m then s:=s+a[i] 17 else begin inc(p); s:=a[i]; end; 18 if p+1>k then l:=m else r:=m; 19 end; 20 writeln(r); 21 end. 22 // 65分。。 23 原程序输出 r ----> 65 24 更改输出为 l+1 ----> 65 25 原程序循环条件 while r-l>1 do if p+1>k then l:=m else r:=m; 26 改为 while l<r do if p+1>k then l:=m+1 else r:=m-1; ------> 55 27 只改成 while l<r 死循环
令我郁闷的是不知上面的程序哪里不对,只有65分。
1 var 2 l,r,mid,n,m,i:longint; 3 a,b:array[1..100000] of longint; 4 function check(p:longint):boolean; 5 var 6 i,pre,tot:longint; 7 begin 8 tot:=m; 9 pre:=0; 10 i:=1; 11 while i<=n do 12 begin 13 if pre+a[i]<=p then 14 begin 15 pre:=pre+a[i]; 16 inc(i); 17 end 18 else 19 begin 20 pre:=0; 21 dec(tot); 22 if tot=0 then exit(false); 23 end; 24 end; 25 exit(true); 26 end; 27 begin 28 readln(n,m); 29 r:=0; 30 for i:=1 to n do 31 begin 32 readln(a[i]); 33 inc(r,a[i]); 34 end; 35 l:=0; 36 while l<r do 37 begin 38 mid:=(l+r)>>1; 39 if check(mid) then r:=mid else l:=mid+1; 40 end; 41 writeln(l); 42 end.
COGS 917 划分序列
交上同样的程序就过了。。
1 var 2 n,k,i,p,l,r,m,s:longint; 3 a:array[1..100000] of longint; 4 begin 5 assign(input,'seqa.in'); 6 reset(input); 7 assign(output,'seqa.out'); 8 rewrite(output); 9 readln(n,k); 10 for i:=1 to n do 11 begin 12 read(a[i]); inc(r,a[i]); 13 end; 14 while r-l>1 do begin 15 m:=(l+r) shr 1; 16 s:=0; p:=0; 17 for i:=1 to n do 18 if s+a[i]<=m then s:=s+a[i] 19 else begin inc(p); s:=a[i]; end; 20 if p+1>k then l:=m else r:=m; 21 end; 22 writeln(r); 23 close(input); 24 close(output); 25 end.