题目:1621: 例7.1(变式) 找数【求最后一个小于等于的数的位置】
http://www.sqyoj.club/problem.php?id=1621
题目:1622: 例7.1 (变式)找数【求第一个大于等于的数的位置】
http://www.sqyoj.club/problem.php?id=1622
【一】
题目:1621: 例7.1(变式) 找数【求最后一个小于等于的数的位置】
http://www.sqyoj.club/problem.php?id=1621
分析:
用lft表示询问区间的左边界,用rght表示询问区间的右边界,[left,rght]组成询问区间
初始化lft=1,rght=n ——这是左闭右闭区间!(重要说明:如果是左闭右开区间,代码是不一样的!!!)
a[0]=-inf,a[n+1]=inf。
序列已经按照升序排好,保证了二分的有序性。
每一次二分:
①取区间中间值mid=(lft+rght)/2;
②判断a[mid]与x的关系:
因为求的是最后一个小于等于x的数的位置,所以从a[mid]<=x入手考虑。
if a[Mid]<=x,由于序列是升序排列,所以取右半区间,修改左边界lft=mid+1,从而lft左边的值都是小于等于x。这里出现难点,为什么是mid+1,而不是mid,原因在于避免死循环,举例说明,(1+2)/2=1,从而mid始终等于lft,陷入死循环。
else if a[mid]>x,取左半区间,修改右边界rght=mid-1,从而rght右边的值都大于x。
重复执行二分操作直到lft>rght。
下面分析答案的情况:
最终循环结束时,lft=right+1。
而lft左边的都是<=x的,rght右边的都是>x的。
所以答案就是rght。如果rght=0,说明在序列中没有小于等于x的元素,从而输出-1。
AC代码如下:
//例7.1找数(求最后一个小于等于的数的位置)
#include
#include
using namespace std;
int n,m,a[110005],x,lft,rght,mid;
int main(){
//输入
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i];
//初始化
a[0]=-1e9;
for(int i=1;i<=m;i++){
//输入
cin>>x;
//二分答案法
lft=1;
rght=n;
//左闭右闭区间。
//这里请注意,如果是左闭右开,代码会不一样的
while(lft<=rght){//终止时lft=rght+1
mid=(lft+rght)/2;
if(a[mid]<=x)lft=mid+1;//lft左边一定是小于等于x的
else rght=mid-1;//rght右边一定是大于x的
}
//输出
if(rght!=0)cout<
【二】
题目:1622: 例7.1 (变式)找数【求第一个大于等于的数的位置】
http://www.sqyoj.club/problem.php?id=1622
分析:
二分说明:
因为求的是第一介大于等于x的数的位置,所以从a[mid]>=x入手考虑。
if a[Mid]>=x,由于序列是升序排列,所以取左半区间,修改左边界rght=mid-1,从而rght右边的值都大于等于x。
else if a[mid]
答案说明:
最终循环结束时,lft=right+1。
而lft左边的都是
所以答案就是lft。如果lft=n+1,说明在序列中没有大于等于x的元素,从而输出-1。
AC代码:
//例7.1找数(求第一个大于等于的数的位置)
#include
#include
using namespace std;
int n,m,a[110005],x,lft,rght,mid;
int main(){
//输入
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i];
//初始化
a[0]=-1e9;
a[n+1]=1e9;
for(int i=1;i<=m;i++){
//输入
cin>>x;
//二分答案法
lft=1;
rght=n;
//左闭右闭区间。
//这里请注意,如果是左闭右开,代码会不一样的
while(lft<=rght){//终止时lft=rght+1
mid=(lft+rght)/2;
if(a[mid]>=x)rght=mid-1;//rght右边一定是大于等于x的
else lft=mid+1;//rght左边一定是小于x的
}
//输出
if(lft<=n)cout<