二分答案.docx

上传人:b****6 文档编号:8038092 上传时间:2023-01-28 格式:DOCX 页数:23 大小:45.56KB
下载 相关 举报
二分答案.docx_第1页
第1页 / 共23页
二分答案.docx_第2页
第2页 / 共23页
二分答案.docx_第3页
第3页 / 共23页
二分答案.docx_第4页
第4页 / 共23页
二分答案.docx_第5页
第5页 / 共23页
点击查看更多>>
下载资源
资源描述

二分答案.docx

《二分答案.docx》由会员分享,可在线阅读,更多相关《二分答案.docx(23页珍藏版)》请在冰豆网上搜索。

二分答案.docx

二分答案

二分答案

有人看到“二分答案”这个题目,可能会很不解。

题目过程可以二分,答案怎么也能二分呢?

事实上,当你看到找极大值中的最小值或者求极小值中的最大值的题目时,二分答案或许是一个不错的选择。

根据数据范围判断是否选用二分(二分能把时间复杂度降到logn),再和上文所说的条件结合,一般就不会错了。

先看一个例题。

【描述】

现在某组织中(记作R)有n个人,他们的联络网形成一棵以Saltless为根的树,有边相连代表两人可以直接联络。

每个人有一个代号,Saltless代号为1,且除Saltless外每个人的父节点的代号小于他自己的代号。

由于某些原因,Saltless给R的成员分别下达紧急任务,R需要分成m组行动,每个组必须满足如下条件:

1、每个组员仅分在本组中

2、至少有一个组员

3、任意两个组员无需通过本组外的人就可以联络(但可以通过本组组员进行联络)

每个人有一个能力指数,一个组的能力指数是全组人能力指数之和。

对于任意一种正确的分组,平均度就是m组中最小能力指数。

为了分组较为平均,Saltless希望平均度尽可能大。

【格式】

PROGRAMNAME:

organ

INPUTFORMAT:

第一行为三个数:

n,m,和Saltless的能力指数(1<=m<=n<=10000)。

接下来n-1行,每行两个数:

此人的父节点代号和他的能力指数(能力指数值为正整数,不超过30,行数就是他本身的代号)

OUTPUTFORMAT:

输出格式:

一个数,表示最大的平均度。

SAMPLEINPUT(organ.in)

722

14

15

21

22

34

43

SAMPLEOUTPUT(organ.out)

10

【Hint】

分组:

{1,3,6},{2,4,5,7}

看到这个题,出口很明显,可能首先想到DFS。

但是10000的数据DFS显然是不能承受的。

这时候我们采用二分答案。

我们先记录下所有人能力指数之和tot,然后假设它的中间值mid就是我们要找的最优解。

然后根据这个下限进行分组,把得到的组数num和m进行比较,如果num>m则表示分组过多,则答案在mid+1到tot的区间内;如果num

当左右指针重合时,最优解也就找到了。

参考代码:

1programsai;

2var

3m,n,tot:

longint;

4mid,num,l,r:

longint;

5i:

integer;

6p,w,t:

array[0..10000]oflongint;

7functionget(x:

longint):

longint;//分组

8var

9i,num:

integer;

10begin

11num:

=0;

12t:

=w;

13fori:

=ndownto1do//从儿子找起,以消除后效性

14ift[i]

=t[p[i]]+t[i]

15elseinc(num);//如果一个组的能力指数之和达到了中间值就加一组

16exit(num);

17end;

18begin

19assign(input,'sai.in');

20reset(input);

21assign(output,'sai.out');

22rewrite(output);

23readln(n,m,tot);

24w[1]:

=tot;

25fori:

=2tondo

26begin

27readln(p[i],w[i]);

28tot:

=tot+w[i];

29end;

30l:

=1;

31r:

=tot;

32whilel<=rdo

33begin

34mid:

=(l+r)>>1;//mid记录了临时的最优解

35num:

=get(mid);

36ifnum>=mthenl:

=mid+1elser:

=mid-1;//与组数进行比较,确定下一个搜索区间

37end;

38writeln(r);//右指针记录的是最优解

39close(input);

40close(output);

41end.

浅析二分答案

二分答案,顾名思义,大家应该想到了二分查找和二分法对题目的优化,我这里要说的二分答案也是如此。

使用范围:

二分答案必须在满足单调性的情况下才可以使用。

例如下面的一道例题:

一个美丽的夜晚,Bingo和他的MM坐在高高的N骨N堆旁看着天上稀稀拉拉的星星。

Bingo对着MM说:

“我们就像指数函数。

你是底数,我是指数。

不管你是大于一还是大于零小于一,我都可一让我们的幸福指数(不同于上面的指数)到正无穷。

”MM说“你是底数,我是指数,我要顺着你改变”。

Bingo一阵感动,但是,作为一个男人,Bingo怎么能委屈自己的MM呢。

Bingo说:

“不行,我会心疼你的。

”MM说:

“我也会心疼你啊”。

…………

【题目描述】

为了不让Bingo和他的MM谁更心疼,也就是让他们一样心疼对方,给你一个数n,请你找出最小的整数x使得xx的位数大于等于n。

【输入】

一个数,n。

【输出】

一个数x,满足xx的位数大于等于n,x-1x-1的位数小于n。

【样例】

heart.in

11

heart.out

10

【数据规模】

n<=1,000,000,000,000,000,000。

【时间,内存限制】

各个测试点1s,64M。

题目解析:

这道题目是典型的二分答案,因为随着mid的增大,mid^mid的位数会随之增大而增大,因此满足单调性,可以使用二分答案来求解;【代码如下】:

programHeart;

 varl,r,mid:

qword;

     n:

qword;

 begin

   readln(n);

   l:

=0;

   r:

=1shl59;

   whilel<>rdo{注意这里,一个奇怪的跳出,这种跳出的优点在于不会为输出l还是r来就接}

     begin

       mid:

=(l+r)shr1;

       iftrunc(mid*ln(mid)/ln(10))+1{换底公式求出它的位数,注意要加1}>=nthenr:

=midelsel:

=mid+1;

     end;

   writeln(l);

end.

是不是很短,其实二分并不复杂,其实很EASY。

本题的另外一个难点在于怎么由已知的一个十进制的数来求位数。

其实就是高一数学,用上指数函数的换底公式就可以了。

另外需要注意的一点便是二分边界的维护,这个要根据实际情况而定是要打成r:

=mid-1还是r:

=mid;

许多问题不易直接求解,但如果我们却可以判断某个解是否可行.构造函数ok(x),布尔型,若最终答案为ans,当x=ans时,ok(x)=true(反之亦可),则满足了二分的条件.假设有一个高效的求ok(x)的算法,则可以通过二分x来获得ans。

请看下面的题目:

奇怪的函数(xx.pas/c/cpp)

问题描述

使得x^x达到或超过n位数字的最小正整数x是多少?

输入数据

输入一个正整数n。

输出数据

输出使得x^x达到n位数字的最小正整数x。

输入样例

11

输出样例

10

时间限制

各测试点1秒

数据规模

n<=2000000000

分析:

直接求解不容易.但对于给定的x我们容易知道x^x的位数.且对于递增的x,位数不降.于是可以二分.

下面给出位数的求法:

令Xx=10p,只要求出p即可。

两边取对数:

Xlnx=pln10

所以p=xlnx/ln10,位数即[xlnx/ln10]+1

代码:

constoo=2000000000;

var

l,r,m,n:

int64;

functioncheck:

boolean;

begin

m:

=(l+r)shr1;

if(m*ln(m)/ln(10))>=n-1thenexit(true)

elseexit(false);

end;

begin

assign(input,'xx.in');reset(input);

readln(n);

close(input);

l:

=1;r:

=oo;

whilel<>rdo

ifcheckthenr:

=m

elsel:

=m+1;

assign(output,'xx.out');rewrite(output);

writeln(l);

close(output);

end.

 

电话网络(phone.pas/c/pp)

[题目描述]

由于地震使得连接汶川县城电话线全部损坏,假如你是负责将电话线接到震中汶川县城的负责人,汶川县城周围分布着N(1≤N≤1,000)根按1..N顺次编号的废弃的电话线杆,任意两根电话线杆间都没有电话线相连。

一共P(1≤P≤10,000)对电话线杆间可以拉电话线,其余的由于地震使得无法被连接。

第i对电话线杆的两个端点分别为Ai,Bi,它们间的距离为Li(1≤Li≤1,000,000)。

数据中保证每对(Ai,Bi)最多只出现1次。

编号为1的电话线杆已经接人了全国的电话网络,整个县城的电话线全都连到了编号为N的电话线杆上。

也就是说,你的任务仅仅是找一条将1号和N号电话线杆连起来的路径,其余的电话线杆并不一定要连人电话网络。

电信公司决定支援灾区免费为汶川县城连结K(0≤K

对于此外的那些电话线,需要为它们付费,总费用等于其中最长的电话线的长度(每根电话线仅连接一对电话线杆)。

如果需要连接的电话线杆不超过K对,那么总支出为0。

请你计算一下,将电话线引到震中汶川县城最少需要在电话线上花多少钱?

[输入格式]

输入文件的第一行包含三个用空格隔开的整数:

N,P和K。

第二行到第P+1行:

每行分别都为空格隔开的整数:

Ai,Bi和Li。

[输出格式]

输出文件中仅包含一个整数,表示在这项工程上的最小支出。

如果任务不可能完成,则输出-1。

[输入样例]

571

125

314

248

323

529

347

456

[输出样例]

4

分析:

这是一道典型的二分答案题,先确定一个长度下限m,大于m的将为免费的。

这样对给出的图中,若某条边小于m,则赋值为0,否则赋值为1。

求1到N的最短路即可。

如果最短路<=电信公司提供的数目,说明m可行,接下来二分枚举比m小的值,反之二分枚举比m大的值。

代码:

const

maxn           =1000;

maxm           =10000;

oo             =100000000;

var

g,gg           :

array[1..maxn,1..maxn]oflongint;

d              :

array[1..maxm]oflongint;

vis            :

array[1..maxn]ofboolean;

p              :

array[1..maxn]oflongint;

n,m,k,u,v,l,r,mid,i,j:

longint;

functionok(x:

longint):

boolean;

var

i,j,l,r,u,v    :

longint;

begin

fori:

=1tondo

forj:

=1tondo

ifgg[i,j]

ifgg[i,j]<=xtheng[i,j]:

=0elseg[i,j]:

=1;

l:

=1;r:

=1;d[1]:

=1;

fori:

=2tondop[i]:

=oo;

fori:

=2tondovis[i]:

=false;vis[1]:

=true;

whilel<=rdo

begin

u:

=d[l];

forv:

=1tondo

ifp[u]+g[u,v]

begin

p[v]:

=p[u]+g[u,v];

ifnotvis[v]then

begin

inc(r);

d[r]:

=v;

vis[v]:

=true;

end;

end;

vis[u]:

=false;

inc(l);

end;

ifp[n]<=kthenexit(true)elseexit(false)

end;

begin

assign(input,'phone.in');

reset(input);

assign(output,'phone.out');

rewrite(output);

readln(n,m,k);

fori:

=1tondoforj:

=1tondogg[i,j]:

=oo;

fori:

=1tomdo

begin

readln(u,v,gg[u,v]);

gg[v,u]:

=gg[u,v];

ifgg[u,v]>rthenr:

=gg[u,v]

end;

l:

=0;

g:

=gg;

whilel

begin

mid:

=(l+r)shr1;

ifok(mid)then

r:

=midelsel:

=mid+1

end;

ifok(l)thenwriteln(l)elsewriteln(-1);

close(output)

end.

集训第七次考试第四题二分答案

注:

二分应该时目前我用的最少的一种思想,已经两次遇到了。

什么样的题目能用二分做呢?

常见的有在一堆极大值中找最小值或在一堆极小值中找最大值,数据量大于10000为二分答案的提示,二分可以减小答案范围,变成log级别的。

二分的关键就是当l与r相等的时候是最大值,如果当前解比mid大,就在mid和r中找,反之在l和mid中找。

l和r为限定的最大值和最小值。

例题:

【描述】

现在根中有n个人,他们的联络网型成一棵以团藏为根的树,有边相连代表两人可以直接联络。

每个人有一个代号,团藏代号为1,且除团藏外每个人的父节点的代号小于他自己的代号。

晓的活动暂时放缓,团藏要当火影的野心一天比一天大,所以他给根的成员分别下达紧急任务,根需要分成m组行动,每个组必须满足如下条件:

1、每个组员仅分在本组中

2、至少有一个组员

3、任意两个组员无需通过本组外的人就可以联络(但可以通过本组组员进行联络)

每个人有一个能力指数(就是贤、力、精、幻、忍、体的综合指数),一个组的能力指数是全组人能力指数之和。

对于任意一种正确的分组,平均度就是m组中最小能力指数。

为了分组较为平均,团藏希望平均度尽可能大。

可怜的佐井迫于团藏的威胁,被要求算出这个最大值,不管怎么说,佐井是个好孩子~而且他只会拿笔画画,不会算这种问题~你就帮帮他好了~~

【格式】

PROGRAMNAME:

sai

INPUTFORMAT:

第一行为三个数:

n,m,和团藏的能力指数(1<=m<=n<=10000)。

接下来n-1行,每行两个数:

此人的父节点代号和他的能力指数(活动程度值为正整数,不超过30,行数就是他本身的代号)

OUTPUTFORMAT:

输出格式:

一个数,表示最大的平均度。

SAMPLEINPUT(filesai.in)

722

14

15

21

22

34

43

SAMPLEOUTPUT(filesai.out)

10

【Hint】

分组:

{1,3,6},{2,4,5,7}

分析:

这个题目描述告诉我们,我们的目标是在保证为m组的情况下,在各组都极大时找一个最小值,而数据量为10000,所以能用二分做。

二分时以每组最小价值为二分依据,设定一个最大价值:

总价值divm+1,一个最小价值1;二分mid:

=(l+r)shr1;判断当价值为mid时能达到的组数,如果小于要求组数,就证明价值太大了,令r等于mid反之令l等于mid,一直到l与r相等就找到了最优解。

代码:

var

  n,m,l,r,i,mid,zhi:

longint;

  s,f,sc:

array[1..10000]oflongint;

functioncal(x:

longint):

longint;{计算以x为答案时组数}

var

  i,now,ans:

longint;

begin

sc:

=s;

ans:

=0;

fori:

=ndownto1do{依然是倒序}

  ifsc[i]>=xthen{只要当前父节点已经能满足值为x,就算一组}

   begin

    inc(ans);{ans为组数}

    now:

=f[i];{递归减去已经抛开的一枝}

    whilenow<>0do

     begin

      sc[now]:

=sc[now]-sc[i];

      now:

=f[now];

     end;

   end;

exit(ans);

end;

begin

readln(n,m,zhi);

s[1]:

=zhi;{s数组记录当前父节点的所有儿子的价值与其之和}

fori:

=2tondo

begin

  readln(f[i],zhi);

  s[i]:

=zhi;

end;

fori:

=ndownto2do{计算所有父节点的总值,倒序消除后效型}

   inc(s[f[i]],s[i]);

ifm=1then

begin

  writeln(s[1]);

  exit;

end;{特殊情况}

l:

=1;

r:

=s[1]divm+1;{先找一个可能的最大值和最小值,然后逐步

                         缩小答案范围,这是二分答案的关键}

whiler-l>1do{一直找到r与l相等就证明已经找到了最优解}

begin

  mid:

=(l+r)div2;{二分,效率为log级别}

  ifcal(mid)

=midelsel:

=mid;{cal计算以mid为答案时的组数

                                         如果组数小,就证明值太大,

                                         反之就是值太小,变换l,r}

end;

writeln(l);

end.

pku3657:

很有意思的题,大意是给你若干句形如a,b,c的描述,表示a~b的最小值为c,要你求第一句出现矛盾的话。

乍一看没什么好想法,模拟吧,不可能,线段树吧,判不出,怎么办?

回过头想想,为什么麻烦了,麻烦在了哪里,依我看,就是麻烦在了"求第一句出现矛盾的话"上,"第一句"会卡死了我们的思路,怎么解决呢?

二分答案!

二分的性质很显然,解会是连续的,而且二分答案之后就可以排序了,此为此题收获之一,二分答案以获得更多信息和更宽松的条件

好了,排完序之后容易想到按c从大到小排序,有如下两种情况可能产生矛盾:

如果一段区间的最小值已知为c1了,那么

此为此题收获之二,细致的读题是ac的必要条件。

第二种情况我自己就没有想到。

这个染色的过程用什么东西来实现?

线段树?

麻烦。

模拟?

不予评价。

这都不是最佳选择。

由于每个点只能被染色一次,那么并查集就能够胜任这个过程了。

具体方法就是每个点的f[i]记录当前它被一整段区间所覆盖的最左边那个点,这样,每次给区间染色就可以用并查集做到均摊O(alpha(n))的了。

并查集的妙用,此为此题收获之三

看这个神奇的数据

103

1101

115

10104

输出3的同学要注意了,普通的离散化不能区分[a,a]U[b,b]和[a,b],那么我们离散化的时候往[a,b](a+1

离散化时的细节(好像离散化经常要插点啊什么的),此为此题收获之四

代码:

viewplaincopytoclipboardprint?

programsyj;{erfenans+rangecovering_usingbingchaset}

const

maxn=25000+5;

varn,q,nn,l,r,mid,i:

longint;

x,c,p,f,color:

array[0..maxn*4]oflongint;

a:

array[1..maxn]oflongint;

proceduresort1(l,r:

longint);

vari,j,k,z:

longint;

begin

i:

=l;j:

=r;k:

=x[(l+r)div2];

repeat

whilex[i]

whilex[j]>kdodec(j);

ifi<=jthenbegin

z:

=x[i];x[i]:

=x[j];x[j]:

=z;

z:

=c[i];c[i]:

=c[j];c[j]:

=z;

inc(i);dec(j);

end;

untili>j;

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 人文社科 > 军事政治

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1