二分答案

有人看到“二分答案”这个题目,可能会很不解。题目过程可以二分,答案怎么也能二分呢?

      事实上,当你看到找极大值中的最小值或者求极小值中的最大值的题目时,二分答案或许是一个不错的选择。根据数据范围判断是否选用二分(二分能把时间复杂度降到log n),再和上文所说的条件结合,一般就不会错了。

先看一个例题。

 

【描述】
现在某组织中(记作R)有n个人,他们的联络网形成一棵以Saltless为根的树,有边相连代表两人可以直接联络。
每个人有一个代号,Saltless代号为1,且除Saltless外每个人的父节点的代号小于他自己的代号。
由于某些原因,Saltless给R的成员分别下达紧急任务,R需要分成m组行动,每个组必须满足如下条件:
	1、每个组员仅分在本组中
	2、至少有一个组员
	3、任意两个组员无需通过本组外的人就可以联络(但可以通过本组组员进行联络)

每个人有一个能力指数,一个组的能力指数是全组人能力指数之和。
对于任意一种正确的分组,平均度就是m组中最小能力指数。为了分组较为平均,Saltless希望平均度尽可能大。

【格式】
PROGRAM NAME: organ

INPUT FORMAT: 
第一行为三个数:n,m,和Saltless的能力指数(1<=m<=n<=10000)。
接下来n-1行,每行两个数:此人的父节点代号和他的能力指数(能力指数值为正整数,不超过30,行数就是他本身的代号) 

OUTPUT FORMAT: 
输出格式: 一个数,表示最大的平均度。

SAMPLE INPUT (organ.in) 
7 2 2
1 4
1 5
2 1
2 2
3 4
4 3

SAMPLE OUTPUT (organ.out) 
10 


【Hint】
分组:{1,3,6},{2,4,5,7}

 

 

 

看到这个题,出口很明显,可能首先想到DFS。但是10000的数据DFS显然是不能承受的。这时候我们采用二分答案。

我们先记录下所有人能力指数之和tot,然后假设它的中间值mid就是我们要找的最优解。然后根据这个下限进行分组,把得到的组数numm进行比较,如果num>m则表示分组过多,则答案在mid+1tot的区间内;如果num<m则表示分组过少,则答案在1mid的区间内;如果num=m则在mid+1tot的区间内寻找更优解。当左右指针重合时,最优解也就找到了。

 

参考代码:

 

  
  
1 program sai;
2 var
3 m,n,tot:longint;
4 mid,num,l,r:longint;
5 i:integer;
6 p,w,t: array [ 0 .. 10000 ] of longint;
7 function get(x:longint):longint; // 分组
8 var
9 i,num:integer;
10 begin
11 num: = 0 ;
12 t: = w;
13 for i: = n downto 1 do // 从儿子找起,以消除后效性
14 if t[i] < x then t[p[i]]: = t[p[i]] + t[i]
15 else inc(num); // 如果一个组的能力指数之和达到了中间值就加一组
16 exit(num);
17 end ;
18 begin
19 assign(input, ' sai.in ' );
20 reset(input);
21 assign(output, ' sai.out ' );
22 rewrite(output);
23 readln(n,m,tot);
24 w[ 1 ]: = tot;
25 for i: = 2 to n do
26 begin
27 readln(p[i],w[i]);
28 tot: = tot + w[i];
29 end ;
30 l: = 1 ;
31 r: = tot;
32 while l <= r do
33 begin
34 mid: = (l + r) >> 1 ; // mid记录了临时的最优解
35 num: = get(mid);
36 if num >= m then l: = mid + 1 else r: = mid - 1 ; // 与组数进行比较,确定下一个搜索区间
37 end ;
38 writeln(r); // 右指针记录的是最优解
39 close(input);
40 close(output);
41 end .  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值