首页 > 代码库 > k序列和

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.
100

 

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.
100

 

k序列和