1.思路:在答案区间二分找答案
2.二分查找:复杂度logn
(1)查找区间单调排序
(2)取中点判断答案所在的区间,进而缩小区间;
(3)继续查找知道找到答案
(4)循环终止的条件有很多,依情况而定(如:右端点小于左端点,达到精度,查找成功等);
3.两个函数
(1)upper_bound(a,a+5,7) 返回数组a中第一个大于7的元素下标
(2)lower_bound(a,a+5,7) 返回数组a中第一个大于等于7的元素下标;
4.eg:
求零点:
已知 f(x) = x+5sin(0.9x-2)-3 在 (2,3) 单调递增,且存在唯一的零点,求该零点(精确到4位小数)
double l,r,mid;
l=2.0;r=3.0;
while(l<r){
if (r-l<=1e-7) break;
mid=(l+r)/2.0;
if (f(mid)>0)
r=mid;
else
l=mid;
}
printf("%.4lf\n",mid);
题目:
problem A:二分查找
nefu 956
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
int a[1000005];
int seek(int l,int r,int x )
{
int mid;
while(r>=l)
{
mid=(l+r)/2;
if(x==a[mid])return mid+1;
else if(x>a[mid]) l=mid+1;
else r=mid-1;
}
return l;
}
int main()
{
int n,x,l,r,i,mid;
while( scanf("%d%d",&n,&x)!=EOF)
{
for(i=0;i<n;i++)
cin>>a[i];
l=0,r=n-1;
mid=seek(l,r,x);
printf("%d\n",mid);
}
return 0;
}
problem B:小清新切绳子-二分
nefu 1648
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
int a[10010],i,l,r,s,k,n,m,ans;
bool judge(int m)
{
s=0;
for(i=0;i<n;i++)
s=s+a[i]/m;
return s>=k;/*bool定义的函数,来判断s>=k是否成立,成立非零,不成立返回零;
s>k可能表示m值有点小;
s=k可能表示m值正确;
都再次循环进一步缩小范围、验证;*/
}
int main()/*此题是找最小值的最大值*/
{
ios::sync_with_stdio(false);
while(cin>>n>>k)
{
for(i=0;i<n;i++)
cin>>a[i];
l=1,r=10000000;
while(l<=r)
{
m=(l+r)/2;/*最开始从1到10000000取中间值通过judge判断是否可以作为分割长度*/
if(judge(m)) ans=m,l=m+1;/*若可以作为分割长度,从右边取值来进一步精确长度,并用ans定住m*/
else r=m-1;/*如不可以,说明m取值太大不能分割,则从左边取值进一步缩小长度*/
}
cout<< ans
<<endl;
}
return 0;
}
problem C:小清新的二倍问题加强版-二分-桶排
nefu 1647
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
int a[10010],n,i,j,flag;
int seek(int l,int r,int x)
{int mid;
while(l<=r)
{
mid=(l+r)/2;
if(a[mid]==x)return 1;
else if(x<a[mid])r=mid-1;
else l=mid+1;
}
return 0;
}
int main()
{
ios::sync_with_stdio(false);/*使cin变得与scanf差不多快*/
cin>>n;
while(n--)
{ i=0;
while(cin>>a[i]&&a[i])
i++;
sort(a,a+i);
flag=0;
for(j=0;j<i;j++)
flag=flag+seek(0,i-1,2*a[j]);
printf("%d\n",flag);
}
return 0;
}
problem D:小清新的二分查找之旅
nefu 1646
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
int a[1000000],b[1000000];
int seek(int l,int r,int x)
{
int mid;
while(r>=l)
{
mid=(l+r)/2;
if(x==a[mid])return mid;
else if(x>a[mid]) l=mid+1;
else r=mid-1;
}
return -1;
}
int main()
{
int n,l,r,p,i,mid;
while( scanf("%d%d",&n,&p)!=EOF)
{
for(i=0;i<n;i++)
scanf("%d",&a[i]);
for(i=0;i<p;i++)
scanf("%d",&b[i]);
l=0,r=n-1;
for(i=0;i<p;i++)
{
mid=seek(l,r,b[i]);
if(mid==-1)printf("YES\n");
else printf("no\n");
}
}
return 0;
}
problem E:小清新的函数坐标-二分
nefu 1645
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
double F (double x)
{
return 0.0001*x*x*x*x*x + 0.003*x*x*x + 0.5*x - 3;
}
double seek(double l,double r,double y)
{ double mid;
while(r-l>0.00001)
{
mid=(l+r)/2;
if(F(mid)==y)return mid;
else if(F(mid)>y)r=mid;
else l=mid;
}
return mid;
}
int main()
{
double x,y;
while(scanf("%lf",&y)!=EOF)
{
x=seek(-20,20,y);
printf("%.4lf\n",x);
}
return 0;
}
problem F:二分查找加强版
nefu 1245
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
int a[2000005];
int main()
{
int n,x,s,i;
while(scanf("%d%d",&n,&x)!=EOF)
{
for(i=0;i<n;i++)
{
scanf("%d",&a[i]);
}
sort(a,a+n);
s=upper_bound(a,a+n,x)-a;
printf("%d\n",s) ;
}
return 0;
}
problem G:切绳子实数版-二分
nefu 1751
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
double a[10010];
int l,r,m,ans,b[10010];
int i,n,k,s;
bool judge(int m)
{
s=0;
for(i=0;i<n;i++)
s=s+b[i]/m;/*m不可为零*/
return s>=k;
}
int main()/*此题是找最小值的最大值*/
{
ios::sync_with_stdio(false);
while(cin>>n>>k)
{
for(i=0;i<n;i++)
{ cin>>a[i];
b[i]=a[i]*100;}
l=0,r=10000000;/*是实数必须从0开始*/
while(l<=r)
{
m=(l+r)/2;
if(m==0){ans=m;break;}
/*因为精度问题当n=1,k=10000时m=0,在judge会出错RE*/
if(judge(m)) ans=m,l=m+1;
else r=m-1;
}
printf("%.2lf\n",ans/100.00);
/*直接用double输入输出会自动向上取整,结果不正确,所以都乘100,最后再除以100*/
}
return 0;
}
problem H:卖古董-DP-二分
NEFU 1211
#include <iostream>
#include <bits/stdc++.h>
#include <algorithm>
const int N=1e5+10;
using namespace std;
int a[N];
int l,r,m,ans,t;
int i,n,k,s,flag;
bool judge(int m)
{
s=0;flag=1;
for(i=1;i<=n;i++)
{if(s+a[i]<=m)s=s+a[i];/*当等于m时,仅为总值的一半,万一是只剩一个古董且价值是总值一半,就不能判断是不是最大值,所以要加等号*/
else s=a[i],flag++;
}
return(flag<=k);/*如果可以作为最大值,那么假设剩下一天卖一个,总天数应小于等于k(中间可以有天数不买);
若总天数大于k则不能判断是否可以作为最大值,则需扩大范围*/
}
int main()/*此题是找最大值的最小值*/
{
ios::sync_with_stdio(false);
cin>>t;
while(t--)
{
cin>>n>>k;
l=0;r=0;
for(i=1;i<=n;i++)
{cin>>a[i];
r=r+a[i];
l=max(l,a[i]);
}
/*卖出古董价值和的最大的那天价值和肯定大于单个古董的最大值且小于所有之和*/
while(l<=r)
{
m=(l+r)/2;/*取中值再判断此中值是否可以为古董价值和最大值*/
if(judge(m)){ans=m;r=m-1;}/*对于有嫌疑的m值,m还可以再减小,取左边,进一步缩小范围并找其最小值*/
else l=m+1;/*对于不是的证明m取小了,取右边再判断*/
}
printf("%d\n",ans);
}
return 0;
}