二分是一个很基础的算法,但是我到现在每次用时,对于边界都还有一些不确定。于是做一个总结,希望自己不再迷糊。
整理内容借鉴了网上搜到的一篇不错的博文,但是代码什么的还是我自己写的。不过本着尊重博主的原则,我还是放一下链接:http://www.cnblogs.com/luoxn28/p/5767571.html(我的和他的还是不同的···)
二分的几种写法:
我们假设一个最简单的环境,对于数x,按要求询问它在一个数组a(假设从小到大)里的位置,数组个数为n。
1.查找最后一个小于x的数的位置
常见的一种要求。返回小于x的其中的最右边一个数的下标。
代码:
int half(int x)
{
int l=0,r=n,mid;
while(l<=r)
{
mid=(l+r)>>1;
if(x>a[mid])
l=mid+1;
else
r=mid-1;
}
return r;
}
2.查找最后一个小于或等于x的数的位置
返回小于或等于x的其中的最右边一个数的下标。
int half(int x)
{
int l=0,r=n,mid;
while(l<=r)
{
mid=(l+r)>>1;
if(x>=a[mid])
l=mid+1;
else
r=mid-1;
}
return r;
}
3.查找第一个大于x的数的位置
常见的一种要求。返回大于x的其中的最左边一个数的下标。
int half(int x)
{
int l=0,r=n,mid;
while(l<=r)
{
mid=(l+r)>>1;
if(x>=a[mid])
l=mid+1;
else
r=mid-1;
}
return l;
}
4.查找第一个大于或等于x的数的位置
常见的一种要求。返回大于或等于x的其中的最左边一个数的下标。
int half(int x)
{
int l=0,r=n,mid;
while(l<=r)
{
mid=(l+r)>>1;
if(x>a[mid])
l=mid+1;
else
r=mid-1;
}
return l;
}
二分总结:
以上几种写法是比较常见基础的。我们会发现二分的写法有几个套路:while(l<=r),l=mid+1,r=mid-1等等。但最重要的是根据题目要求来定边界的处理,有时候你二分的并不一定是整数,更多情况下,你的判断mid并不只是简单的判断大小,可能会写一大堆复杂的判断过程。
二分之外,还有三分,可以看做是对二分的一种改进,适用于单峰函数。
练习题:
附几道二分练习题。
1、Aggressive cows(来源:POJ2456),纯二分, 题目链接:http://poj.org/problem?id=2456。
2、Cable master(来源:POJ 1064)链接:http://poj.org/problem?id=1064
3、Monthly Expense(来源:POJ 3273)链接: http://poj.org/problem?id=3273
4、关押罪犯 (NOIP2010提高组),纯二分,唯一的难度在于check。
5、聪明的质监员(NOIP2011提高组),纯二分,附上我的讲解:http://blog.csdn.net/g19zwk/article/details/77371970。
6、借教室(NOIP2012提高组),二分加线段树,有一定难度。
7、传送带(来源:1857: [Scoi2010])链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1857,这是一道三分题。
以上题目,排名顺序与难度大小无关(>_<)如果你没有评测渠道,我又没有附链接,那么可以去洛谷评测,NOIP题它都有。
转载来源:千古的博客
本篇完。