【题目链接】
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 x≤a1,那么与x最接近的数字是 a 1 a_1 a1。
- a中最大的数字为 a n a_n an,如果 x ≥ a n x \ge a_n x≥an那么与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} ai≤x<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 al−1≤x<al
解法2:二分查找满足小于等于x的最后一个元素
通过二分查找,找出满足条件小于等于x的最后一个元素的下标为r,r即为上述的i,r+1为i+1,因此 a r ≤ x < a r + 1 a_{r}\le x < a_{r+1} ar≤x<ar+1
而后进行判断:
- 如果 ∣ x − a i ∣ > ∣ x − a i + 1 ∣ |x-a_i| > |x-a_{i+1}| ∣x−ai∣>∣x−ai+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}| ∣x−ai∣≤∣x−ai+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;
}