信息学奥赛一本通 1240:查找最接近的元素 | OpenJudge NOI 1.11 01:查找最接近的元素

【题目链接】

ybt 1240:查找最接近的元素
OpenJudge NOI 1.11 01:查找最接近的元素

【题目考点】

1. 二分
  • 二分查找满足条件的第一个元素
  • 二分查找满足条件的最后一个元素

【解题思路】

一个非降序列a中有n个数字。要查找a中与数字x最接近的数字。

  • a中最小的数字为 a 1 a_1 a1,如果 x ≤ a 1 x \le a_1 xa1,那么与x最接近的数字是 a 1 a_1 a1
  • a中最大的数字为 a n a_n an,如果 x ≥ a n x \ge a_n xan那么与x最接近的数字是 a n a_n an
  • 如果 a 1 < x < a n a_1 < x < a_n a1<x<an,那么序列a中一定存在相邻两个数字 a i a_i ai a i + 1 a_{i+1} ai+1,满足 a i ≤ x < a i + 1 a_i \le x < a_{i+1} aix<ai+1

以下有三种方法确定 a i a_i ai a i + 1 a_{i+1} ai+1

解法1:二分查找满足大于x的第一个元素

通过二分查找,找出满足条件大于x的一个元素的下标为l,l即为上述的i+1,l-1为i,因此 a l − 1 ≤ x < a l a_{l-1}\le x < a_l al1x<al

解法2:二分查找满足小于等于x的最后一个元素

通过二分查找,找出满足条件小于等于x的最后一个元素的下标为r,r即为上述的i,r+1为i+1,因此 a r ≤ x < a r + 1 a_{r}\le x < a_{r+1} arx<ar+1

而后进行判断:

  • 如果 ∣ x − a i ∣ > ∣ x − a i + 1 ∣ |x-a_i| > |x-a_{i+1}| xai>xai+1,那么 a i + 1 a_{i+1} ai+1是最接近x的数字。
  • 如果 ∣ x − a i ∣ ≤ ∣ x − a i + 1 ∣ |x-a_i| \le |x-a_{i+1}| xaixai+1,那么 a i a_i ai是最接近x的数字。

【题解代码】

以下代码只选用多种二分查找中的一种写法

解法1: 二分查找满足大于x的第一个元素
#include <bits/stdc++.h>
using namespace std;
int a[100005];
int main()
{
    int n, x, l, r, m;
    cin >> n;
    for(int i = 1; i <= n; ++i)
        cin >> a[i];
    cin >> m;
    while(m--)//m组查询 
    {
        cin >> x;
        if(x <= a[1])
            cout << a[1] << endl;
        else if(x >= a[n])
            cout << a[n] << endl;
        else
        {
            l = 1, r = n;
            while(l < r)
            {
            	int m = (l+r)/2;
            	if(a[m] > x)
            		r = m;
            	else
            		l = m+1;
			}
            if(abs(a[l-1]-x) <= abs(a[l]-x))//此时a[l-1] <= x < a[l]
                cout << a[l-1] << endl;
            else
                cout << a[l] << endl;
        }
    }
    return 0;
}
解法2:二分查找满足小于等于x的最后一个元素
  • 写法1:查找结果在[l,r]范围内
#include <bits/stdc++.h>
using namespace std;
int a[100005];
int main()
{
    int n, x, l, r, m;
    cin >> n;
    for(int i = 1; i <= n; ++i)
        cin >> a[i];
    cin >> m;
    while(m--)//m组查询 
    {
        cin >> x;
        if(x <= a[1])
            cout << a[1] << endl;
        else if(x >= a[n])
            cout << a[n] << endl;
        else
        {
            l = 1, r = n;
            while(l < r)
            {
            	int m = (l+r+1)/2;
				if(a[m] <= x)
					l = m;
				else
					r = m-1;
			}
            if(abs(a[l]-x) <= abs(a[l+1]-x))//此时a[l] <= x < a[l+1]
                cout << a[l] << endl;
            else
                cout << a[l+1] << endl;
        }
    }
    return 0;
}
  • 写法2:查找结果在[l,r)范围内
#include <bits/stdc++.h>
using namespace std;
int a[100005];
int main()
{
    int n, x, l, r, m;
    cin >> n;
    for(int i = 1; i <= n; ++i)
        cin >> a[i];
    cin >> m;
    while(m--)//m组查询 
    {
        cin >> x;
        if(x <= a[1])
            cout << a[1] << endl;
        else if(x >= a[n])
            cout << a[n] << endl;
        else
        {
            l = 1, r = n+1;
            while(l+1 < r)
            {
            	int m = (l+r)/2;
				if(a[m] <= x)
					l = m;
				else
					r = m;
			}
            if(abs(a[l]-x) <= abs(a[r]-x))//此时a[l] <= x < a[r]
                cout << a[l] << endl;
            else
                cout << a[r] << endl;
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值