二分查找
请在一个有序递增数组中(不存在相同元素),采用二分查找,找出值x的位置,如果x在数组中不存在,请输出-1!
输入格式
第一行,一个整数n,代表数组元素个数(n <= 600000)
第二行,n个数,代表数组的n个递增元素(1<=数组元素值<=2000000)
第三行,一个整数x,代表要查找的数(0<=x<=2000000)
输出格式
按题意输出位置或者-1。
输入/输出例子1
输入:
10
1 3 5 7 9 11 13 15 17 19
3
输出:
2
代码:
#include<bits/stdc++.h>
using namespace std;
int n,k,a[1000005];
int ff(int a[],int k)
{
int l=1,r=n,m=0,bj=-1;
while(l<=r)
{
m=(l+r)/2;//中间位置
if(a[m]==k){bj=m+1;break;}
if(a[m]>k)r=m-1;//在右半部分
if(a[m]<k)l=m+1;//在左半部分
}
return bj;
}
int main(){
cin>>n;
for(int i=0;i<n;i++)
cin>>a[i];
cin>>k;
cout<<ff(a,k);
return 0;
}
二分查找左侧边界
请在一个有序不递减的数组中(数组中有相等的值),采用二分查找,找到值x第1次出现的位置,如果不存在x请输出-1。
请注意:本题要求出q个x,每个x在数组中第一次出现的位置。
比如有6个数,分别是:1 2 2 2 3 3,那么如果要求3个数:3 2 5,在数组中第一次出现的位置,答案是:5 2 -1。
输入格式
第一行,一个整数n,代表数组元素个数(n <= 105)
第二行,n个整数,用空格隔开,代表数组的n个元素(1<=数组元素的值<=108)
第三行,一个整数q,代表有要求出q个数首次出现的位置(q<=105)
第四行,q个整数,用空格隔开,代表要找的数(1<=要找的数<=108)
输出格式
输出1行,含q个整数,按题意输出要找的每个数在数组中首次出现的位置,如果不存在这样的数,请输出-1。
输入/输出例子1
输入:
6
1 2 2 2 3 3
3
3 2 5
输出:
5 2 -1
代码:
#include<bits/stdc++.h>
using namespace std;
int n,k,a[10000005],x;
int main(){
cin>>n;
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
cin>>k;
while(k--){
scanf("%d",&x);
int l=0,r=n+1,m=0;
while(l+1<r)
{
m=(l+r)/2;
if(a[m]<x)l=m;
else r=m;
}
if(a[r]==x)printf("%d ",r);
else printf("-1 ");
}
return 0;
}
二分查找右侧边界
请在一个有序不递减的数组中(数组中的值有相等的值),采用二分查找,找到最后1次出现值x的位置,如果不存在x请输出-1。
比如有6个数,分别是:1 2 2 2 3 3,那么如果要求3个数:3 2 5,在数组中最后一次出现的位置,答案是:6 4 -1。
输入格式
第一行,一个整数n,代表数组元素个数(n <= 105)
第二行,n个整数,用空格隔开,代表数组的n个元素(1<=数组元素的值<=108)
第三行,一个整数q,代表有要求出q个数最后一次出现的位置(q<=105)
第四行,q个整数,用空格隔开,代表要找的数(1<=要找的数<=108)
输出格式
按题意输出位置或者-1。
输入/输出例子1
输入:
6
1 2 2 2 3 3
3
3 2 5
输出:
6 4 -1
代码:
#include<bits/stdc++.h>
using namespace std;
long long n,q,a[100005],x;
int main(){
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
cin>>q;
while(q--)
{
scanf("%lld",&x);
int l=1,r=n+1,mid=0;
while(l<r)
{
mid=(l+r)/2;
if(a[mid]<=x)l=mid+1;
else r=mid;
}
if(a[l-1]==x) printf("%d ",l-1);
else printf("-1 ");
}
return 0;
}
前面几道都是经典例题,下面我们来看看几道二分查找的实际运用
神奇的猴子(monkey)
小祖是一个爱冒险的猴子,一天它来到一个村庄,顿时,有n(1<=n<=100000)个小怪冒了出来,想把它吃了,每个小怪都有它的体力值hp(1<=hp<=maxlongint)。小祖手上有m(1<=m<=100000)个炸弹,每个炸弹威力为k(1<=k<=maxlongint),可以炸死体力小于炸弹威力所有小怪。现在它想知道,它的每一个炸弹能炸剩多少个小怪,请你编一个程序帮助它吧。
输入格式
第一行,输入一个n,代表有n个小怪n(1<=n<=100000)
第二行,有n个数,代表每个小怪的体力值hp(1<=hp<=maxlongint)
第三行,输入一个m,代表有m个炸弹m(1<=m<=100000)
接下来m行,每行一个数,代表每个炸弹威力k(1<=k<=maxlongint)
输出格式
有m行,每行一个数代表它的每一个炸弹能炸剩多少个小怪。
输入/输出例子1
输入:
5
1 2 2 2 3
3
2
1
3
输出:
4
5
1
提示:
这道题其实就是找某个数第一次出现的位置,再用n减去这个数的位置
代码:
#include<bits/stdc++.h>
using namespace std;
int n,k,a[10000005],x;
int main(){
cin>>n;
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
cin>>k;
while(k--){
scanf("%d",&x);
int l=0,r=n+1,m=0;
while(l+1<r)
{
m=(l+r)/2;
if(a[m]<x)l=m;
else r=m;
}
cout<<n-r+1<<"\n";
}
return 0;
}
蓝牙连接
n个人排队签到,队伍成一条直线,签到处位置为0,第i个人与签到处的距离是pi。排队是一件无聊的事情,所以他们可以通过手机蓝牙与附近的人连接聊天,蓝牙信号的有效距离是有限的,每个人只能与他距离不超过d的人进行蓝牙连接,这样这两个人就可以聊天了。已知每个人的位置和蓝牙的有效距离d,请你编程算出有多少组可以相互聊天的人。
输入格式
第1行一个整数n。(2≤n≤1000000)
第2行一个整数d。(0≤d≤1000000)
接下来有n行,每行有一个整数pi(0≤pi≤1 000 00000),表示每一个人与签到处的距离。
输出格式
一个整数,表示队伍中有多少组可以相互聊天的人。
输入/输出例子1
输入:
5 6 1 3 5 11 34
输出:
4
样例解释
队伍中1和5,1和3,5和11,3和5一共4组人可以相互聊天。
代码:
#include<bits/stdc++.h>
using namespace std;
long long n,kk,a[1000005],ans;
int ss(long long u){//找传进来的u的位置
long long x=1,y=n+1,mid=0;
while(x+1<y){
mid=(x+y)/2;
if(a[mid]>u)y=mid;
else x=mid;
}
return x;
}
int main(){
cin>>n>>kk;
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
for(int i=1;i<n;i++)
ans+=ss(a[i]+kk)-i;
cout<<ans;
return 0;
}
选人
在一条坐标轴上,有N头奶牛,第i头奶牛的位置是Xi。FJ现在要选出三头奶牛去比赛,不妨假设选择了奶牛a,b,c。那么必须要满足:
1、Xa < Xb < Xc。
2、 Xb-Xa <= Xc - Xb <= 2 * (Xb - Xa)。
你的任务是计算,FJ总共有多少种不同的选择?
输入格式
第一行,一个整数N。 3 <= N <= 1000。 接下来有N行,第i行是整数Xi。
输出格式
一个整数。
输入/输出例子1
输入:
5 3 1 10 7 4
输出:
4
样例解释
可以有4种不同的选择,每种选择对应的3头奶牛的坐标是: {1,3,7} {1,4,7} {4,7,10} {1,4,10}
代码:
#include<bits/stdc++.h>
using namespace std;
long long n,s,q[10000000],t,m,ans,ans2,l,r;
int main()
{
scanf("%lld",&n);
for(int i=1;i<=n;i++)
scanf("%lld",&q[i]);
sort(q+1,q+1+n);
for(int i=1;i<=n;i++)
{
for(int j=i+1;j<=n;j++)
{
int l=0,r=n+1,m;
while(l+1<r)
{
m=(l+r)/2;
if(q[j]-q[i]>q[m]-q[j])l=m;//符合条件
else r=m;
}
ans=l;
l=0,r=n+1;
while(l+1<r)
{
m=(l+r)/2;
if(q[m]-q[j]>2*(q[j]-q[i]))r=m;//符合条件
else l=m;
}
ans2=l;
t+=ans2-ans;
}
}
cout<<t;
return 0;
}
天堂_珍珠
我有很多很多(n条)用魔法合成的珍珠项链……(其实神仙比凡人更爱美),每天起来我都要从中挑一条戴上……挑哪条很有讲究,如果比情敌的难看,那么就会被(--),如果比天后Hera的好看,那么就完蛋了(--)。所以我希望你能帮帮我,解决这个令人头疼的问题——每天帮我算算,那天我能戴的项链有多少条。
输入格式
第一行为正整数n(项链总条数)。
第二行有n个整数(代表每条项链晶的好看程度Xi,0<=Xi<=maxlongint。)
第三行为正整数m,表示总天数(也就是总询问次数)。
以下m行,每行两个整数Ai,Bi(1<=Ai,Bi<=maxlongint),询问好看程度在Ai到Bi之间的项链条数(含等于Ai或Bi的,Ai与Bi大小关系不确定)。
输出格式
输出m行,对于每次询问输出一行,从Ai到Bi(含Ai,Bi)好看程度在Ai到Bi之间的项链条数。
输入/输出例子1
输入:
7 8 2 3 5 6 7 7 6 1 5 8 6 1 10 5 5 4 4 7 8
输出:
3 4 7 1 0 3
数据范围:对于25%数据,有m,n<=1000。 对于100%数据,有m,n<=100000。
代码:
#include<bits/stdc++.h>
using namespace std;
long long n,m,a[100005],l,r;
long long sa(long long u)
{
long long x=0,y=n+1,mid=0;
while(x+1<y)
{
mid=(x+y)/2;
if(a[mid]>=u)y=mid;
else x=mid;
}
return y;
}
int main(){
scanf("%lld",&n);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
scanf("%lld",&m);
sort(a+1,a+n+1);
for(int i=1;i<=m;i++)
{
scanf("%lld%lld",&l,&r);
if(l>r)swap(l,r);
printf("%lld\n",sa(r+1)-sa(l));
}
return 0;
}
菜鸟代码,大佬勿喷......