二分答案Word文档格式.docx
《二分答案Word文档格式.docx》由会员分享,可在线阅读,更多相关《二分答案Word文档格式.docx(23页珍藏版)》请在冰豆网上搜索。
如果num<
m则表示分组过少,则答案在1到mid的区间内;
如果num=m则在mid+1到tot的区间内寻找更优解。
当左右指针重合时,最优解也就找到了。
参考代码:
1programsai;
2var
3m,n,tot:
longint;
4mid,num,l,r:
5i:
integer;
6p,w,t:
array[0..10000]oflongint;
7functionget(x:
longint):
//分组
8var
9i,num:
10begin
11num:
=0;
12t:
=w;
13fori:
=ndownto1do//从儿子找起,以消除后效性
14ift[i]<
xthent[p[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:
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
【数据规模】
n<
=1,000,000,000,000,000,000。
【时间,内存限制】
各个测试点1s,64M。
题目解析:
这道题目是典型的二分答案,因为随着mid的增大,mid^mid的位数会随之增大而增大,因此满足单调性,可以使用二分答案来求解;
【代码如下】:
programHeart;
varl,r,mid:
qword;
n:
begin
readln(n);
l:
r:
=1shl59;
whilel<
rdo{注意这里,一个奇怪的跳出,这种跳出的优点在于不会为输出l还是r来就接}
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)=false,当x>
=ans时,ok(x)=true(反之亦可),则满足了二分的条件.假设有一个高效的求ok(x)的算法,则可以通过二分x来获得ans。
请看下面的题目:
奇怪的函数(xx.pas/c/cpp)
问题描述
使得x^x达到或超过n位数字的最小正整数x是多少?
输入数据
输入一个正整数n。
输出数据
输出使得x^x达到n位数字的最小正整数x。
输入样例
输出样例
时间限制
各测试点1秒
数据规模
=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:
if(m*ln(m)/ln(10))>
=n-1thenexit(true)
elseexit(false);
end;
assign(input,'
xx.in'
reset(input);
readln(n);
close(input);
l:
r:
=oo;
whilel<
rdo
ifcheckthenr:
=m
elsel:
=m+1;
assign(output,'
xx.out'
rewrite(output);
writeln(l);
close(output);
电话网络(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<
N)对由你指定的电话线杆。
对于此外的那些电话线,需要为它们付费,总费用等于其中最长的电话线的长度(每根电话线仅连接一对电话线杆)。
如果需要连接的电话线杆不超过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;
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:
i,j,l,r,u,v
fori:
=1tondo
forj:
ifgg[i,j]<
oothen
=xtheng[i,j]:
=0elseg[i,j]:
d[1]:
=2tondop[i]:
=2tondovis[i]:
=false;
vis[1]:
=true;
u:
=d[l];
forv:
ifp[u]+g[u,v]<
p[v]then
p[v]:
=p[u]+g[u,v];
ifnotvis[v]then
inc(r);
d[r]:
=v;
vis[v]:
vis[u]:
inc(l);
ifp[n]<
=kthenexit(true)elseexit(false)
phone.in'
phone.out'
readln(n,m,k);
=1tondoforj:
=1tondogg[i,j]:
=1tomdo
readln(u,v,gg[u,v]);
gg[v,u]:
=gg[u,v];
ifgg[u,v]>
rthenr:
=gg[u,v]
g:
=gg;
mid:
ifok(mid)then
=mid+1
ifok(l)thenwriteln(l)elsewriteln(-1);
close(output)
集训第七次考试第四题二分答案
注:
二分应该时目前我用的最少的一种思想,已经两次遇到了。
什么样的题目能用二分做呢?
常见的有在一堆极大值中找最小值或在一堆极小值中找最大值,数据量大于10000为二分答案的提示,二分可以减小答案范围,变成log级别的。
二分的关键就是当l与r相等的时候是最大值,如果当前解比mid大,就在mid和r中找,反之在l和mid中找。
l和r为限定的最大值和最小值。
例题:
现在根中有n个人,他们的联络网型成一棵以团藏为根的树,有边相连代表两人可以直接联络。
每个人有一个代号,团藏代号为1,且除团藏外每个人的父节点的代号小于他自己的代号。
晓的活动暂时放缓,团藏要当火影的野心一天比一天大,所以他给根的成员分别下达紧急任务,根需要分成m组行动,每个组必须满足如下条件:
每个人有一个能力指数(就是贤、力、精、幻、忍、体的综合指数),一个组的能力指数是全组人能力指数之和。
为了分组较为平均,团藏希望平均度尽可能大。
可怜的佐井迫于团藏的威胁,被要求算出这个最大值,不管怎么说,佐井是个好孩子~而且他只会拿笔画画,不会算这种问题~你就帮帮他好了~~
sai
n,m,和团藏的能力指数(1<
此人的父节点代号和他的能力指数(活动程度值为正整数,不超过30,行数就是他本身的代号)
SAMPLEINPUT(filesai.in)
SAMPLEOUTPUT(filesai.out)
分析:
这个题目描述告诉我们,我们的目标是在保证为m组的情况下,在各组都极大时找一个最小值,而数据量为10000,所以能用二分做。
二分时以每组最小价值为二分依据,设定一个最大价值:
总价值divm+1,一个最小价值1;
二分mid:
=(l+r)shr1;
判断当价值为mid时能达到的组数,如果小于要求组数,就证明价值太大了,令r等于mid反之令l等于mid,一直到l与r相等就找到了最优解。
代码:
var
n,m,l,r,i,mid,zhi:
s,f,sc:
array[1..10000]oflongint;
functioncal(x:
{计算以x为答案时组数}
i,now,ans:
sc:
=s;
ans:
=ndownto1do{依然是倒序}
ifsc[i]>
=xthen{只要当前父节点已经能满足值为x,就算一组}
inc(ans);
{ans为组数}
now:
=f[i];
{递归减去已经抛开的一枝}
whilenow<
0do
sc[now]:
=sc[now]-sc[i];
=f[now];
exit(ans);
readln(n,m,zhi);
s[1]:
=zhi;
{s数组记录当前父节点的所有儿子的价值与其之和}
readln(f[i],zhi);
s[i]:
=ndownto2do{计算所有父节点的总值,倒序消除后效型}
inc(s[f[i]],s[i]);
ifm=1then
writeln(s[1]);
exit;
{特殊情况}
=s[1]divm+1;
{先找一个可能的最大值和最小值,然后逐步
缩小答案范围,这是二分答案的关键}
whiler-l>
1do{一直找到r与l相等就证明已经找到了最优解}
=(l+r)div2;
{二分,效率为log级别}
ifcal(mid)<
mthenr:
=mid;
{cal计算以mid为答案时的组数
如果组数小,就证明值太大,
反之就是值太小,变换l,r}
pku3657:
很有意思的题,大意是给你若干句形如a,b,c的描述,表示a~b的最小值为c,要你求第一句出现矛盾的话。
乍一看没什么好想法,模拟吧,不可能,线段树吧,判不出,怎么办?
回过头想想,为什么麻烦了,麻烦在了哪里,依我看,就是麻烦在了"
求第一句出现矛盾的话"
上,"
第一句"
会卡死了我们的思路,怎么解决呢?
二分答案!
二分的性质很显然,解会是连续的,而且二分答案之后就可以排序了,此为此题收获之一,二分答案以获得更多信息和更宽松的条件
好了,排完序之后容易想到按c从大到小排序,有如下两种情况可能产生矛盾:
如果一段区间的最小值已知为c1了,那么<
c1的值在这段区间上是不可能出现的了,就相当于拿到值为ci的一堆区间时(注意,可能不止一个),对整段区间中没有颜色的点染上ci的颜色(哦,不记得说了,先给区间离散化一下),如果最后发现这一段没染到一个,那么就矛盾了;
题目有要求任意两个仓库的存量不能相同,也就是说,对于值同为ci的区间,如果它们的并不是一个整区间的话,显然会出现矛盾(ci的存量要出现在多个地方)。
此为此题收获之二,细致的读题是ac的必要条件。
。
第二种情况我自己就没有想到。
这个染色的过程用什么东西来实现?
线段树?
麻烦。
模拟?
不予评价。
这都不是最佳选择。
由于每个点只能被染色一次,那么并查集就能够胜任这个过程了。
具体方法就是每个点的f[i]记录当前它被一整段区间所覆盖的最左边那个点,这样,每次给区间染色就可以用并查集做到均摊O(alpha(n))的了。
并查集的妙用,此为此题收获之三
看这个神奇的数据
103
1101
115
10104
输出3的同学要注意了,普通的离散化不能区分[a,a]U[b,b]和[a,b],那么我们离散化的时候往[a,b](a+1<
b)中插一个点就可以了。
离散化时的细节(好像离散化经常要插点啊什么的),此为此题收获之四
viewplaincopytoclipboardprint?
programsyj;
{erfenans+rangecovering_usingbingchaset}
maxn=25000+5;
varn,q,nn,l,r,mid,i:
x,c,p,f,color:
array[0..maxn*4]oflongint;
a:
array[1..maxn]oflongint;
proceduresort1(l,r:
longint);
vari,j,k,z:
i:
=l;
j:
=r;
k:
=x[(l+r)div2];
repeat
whilex[i]<
kdoinc(i);
whilex[j]>
kdodec(j);
ifi<
=jthenbegin
z:
=x[i];
x[i]:
=x[j];
x[j]:
=z;
=c[i];
c[i]:
=c[j];
c[j]:
inc(i);
dec(j);
untili>
j;