RT,本文专门讲解所谓的二分搜索。
这个二分搜索其实就是二分查找(Binary Search),效率是log级的,相对普通的n级搜索要快很多。
他的核心思路就是每一次折半,然后看看要找的元素与中点的关系再决定跳到左区间还是右区间
因此,二分搜索的最大前提也是唯一前提就是:所搜索的序列必须得有单调性。
二分的实现不难,首先你需要一个下界和一个上界,作为ls和rs的初始值。
其次,取中间值mid,然和调用检查函数check。这个函数会判断在mid的情况下符不符合要求,如果符合的话那就看左区间(往小的看),不符合就看右区间。
当然要注意,ls和rs的更新种类蛮多,这里是直接更新为mid,也有一些会更新为mid+1和mid-1,具体情况视题目而定。
那么一般的实现如下:
int search(int item){
int ls=low,rs=high,mid;
while(ls<=rs){
mid=(ls+rs)>>1;
if(check(mid)){
ls=mid;
}else{
rs=mid;
}
}
}
关于二分搜索模板的练习做一道就OK了: POJ-3273
这道题的题意大致是有n个数,然后希望把他们分成m组,对于每一组的和,问最大值的最小值为多少?
思路很暴力,首先答案肯定是在有限区间内,那么二分这个区间,假如对于当前的mid可以分组,那么跳左区间,反之跳右区间。
AC代码如下:
#include
#include
#include
using namespace std;
const int maxn=100100;
int n,m;
int a[maxn];
bool check(int mx){
int t=0,cur=a[1];
for(int i=1;i<=n;i++){
if(cur>mx)return false;
if(cur+a[i+1]>mx){
cur=a[i+1];
t++;
}else{
cur+=a[i+1];
}
}
t++;
if(t>m)return false;
return true;
}
int search(){
int l=1,r=1000000000,mid;
while(l<=r){
mid=(l+r)>>1;
if(check(mid))r=mid-1;
else l=mid+1;
}
return l;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
printf("%d",search());
}
二分搜索要掌握一点都不难,但是在题目中熟练运用还是需要一定技巧。
这里列举几道二分搜索的题,一道一道地过。
POJ-3258
这一道题就是跳石头的问题,当然可以从中拿走若干块,然后问你要跳的最小距离的最大值是多少?
其实与上一道题运用的想法都一样,但是要注意:check函数的写法截然不同。
AC的代码如下,可以感受一下两道题的差异:
#include
#include
#include
using namespace std;
const int maxn=50050;
int l,n,m;
int a[maxn];
bool check(int x){
int start=0,t=0;
for(int i=1;i<=n;i++){
if(a[i]-startelse start=a[i];
}
// cout<
if(l-startm)return false;
return true;
}
int search(){
int lt=0,r=l,mid;
while(lt<=r){
mid=(lt+r)>>1;
if(check(mid)){
lt=mid+1;
}else{
r=mid-1;
}
}
return lt;
}
int main(){
scanf("%d%d%d",&l,&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
sort(a,a+n+1);
printf("%d",search()-1);
}
POJ-3122
这道题是我做过的最毒瘤的几道题之一,题意是你有很多个不同半径的圆,要分给n个人以及你自己,每个人分一样的大小,然后问你最大可以分多大。
当然,你不可以把很多个残片拼在一起。
这题有几个坑:
第一个是在于π的值必须足够精确,我就直接调用了计算器的值。
第二个是在于有误差这种鬼东西,记得一定要开够。
最后一个是在于虽然朋友只有n个,却要分给n+1个人(包括你自己),我前x次都是这么错得。
除了这几个毒瘤点,这道题还算不错,AC代码如下:
#include
#include
#include
using namespace std;
const int maxn=50050;
int l,n,m;
int a[maxn];
bool check(int x){
int start=0,t=0;
for(int i=1;i<=n;i++){
if(a[i]-startelse start=a[i];
}
// cout<
if(l-startm)return false;
return true;
}
int search(){
int lt=0,r=l,mid;
while(lt<=r){
mid=(lt+r)>>1;
if(check(mid)){
lt=mid+1;
}else{
r=mid-1;
}
}
return lt;
}
int main(){
scanf("%d%d%d",&l,&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
sort(a,a+n+1);
printf("%d",search()-1);
}
//787
当前时间:2018.6.12 21:54
持续更新中。。。