这几天刷题,写了几次二分,无一例外都写炸了,要么多一个,要么少一个,要么就死循环。。。。
于是总结一下写二分的经验和教训。。。
请看下面一段代码:
int bsearch(int lf,int ri){
if(lf==ri)return lf;
int mid=(lf+ri)/2,ans=0;
if(mid==lf||mid==ri) return mid;
int pos=0<pre class="cpp" name="code">int bsearch(int lf,int ri){
//if(lf==ri)return lf;
int mid=(lf+ri)/2,ans=0;
if(mid==lf||mid==ri){
int ansl=0,ansr=0,pos=0;
for(int i=1;i<=n+1;i++)
if(d[i]-d[pos]<lf) ansl++;
else pos=i;
pos=0;
for(int i=1;i<=n+1;i++)
if(d[i]-d[pos]<ri) ansr++;
else pos=i;
if(ansr<=m)return ri;
else return lf;
}
int pos=0;
for(int i=1;i<=n+1;i++)
if(d[i]-d[pos]<mid) ans++;
else pos=i;
if(ans>m) return bsearch(lf,mid);
return bsearch(mid,ri);
}
这是NOIP2015D2T1跳石头。本来我没有加第四行的代码,但是不加的话就会陷入死循环,加了的话只得了60分。
忽略第四行代码,看一下求mid的过程,我只写了(lf+ri)>>1,而没有写(lf+ri+1)>>1;但是加上的话仍然死循环。
动态查一下错,发现当lf===4,ri==5时,求出的mid==5,然后由于除法向下取整,递归一直保持lf==4,ri==5,mid==5的状态。于是尝试将递归处改为,求mid时不+1:
if(ans>=m) return bsearch(lf,mid-1);
return bsearch(mid,ri+1);
然而结果是这样的
很明显,4459/4460是边界搞错,第二个点就TLE说明死循环
如果在求mid时加一,就变成了这个样子:
没有了死循环,但是边界还是搞错。
这时候可以特判一下,同时取消+1或-1之类的操作:
int bsearch(int lf,int ri){
int mid=(lf+ri)/2,ans=0;
if(mid==lf||mid==ri){
int ansl=0,ansr=0,pos=0;
for(int i=1;i<=n+1;i++)
if(d[i]-d[pos]<lf) ansl++;
else pos=i;
pos=0;
for(int i=1;i<=n+1;i++)
if(d[i]-d[pos]<ri) ansr++;
else pos=i;
if(ansr<=m)return ri;
else return lf;
}
int pos=0;
for(int i=1;i<=n+1;i++)
if(d[i]-d[pos]<mid) ans++;
else pos=i;
if(ans>m) return bsearch(lf,mid);
return bsearch(mid,ri);
}
于是成功AC