题面:
在一个非降序列中,查找与给定值最接近的元素。
输入
第一行包含一个整数n,为非降序列长度。1 <= n <= 100000。
第二行包含n个整数,为非降序列各元素。所有元素的大小均在0~1000000000(1e9)之间。
第三行包含一个整数m,为要询问的给定值个数。1 <= m <= 100000。
接下来m行,每行一个整数,为要询问最接近元素的给定值。所有给定值的大小均在0~1000000000(1e9)之间。
输出
m行,每行一个整数,为最接近相应给定值的元素值,保持输入顺序。若有多个值满足条件,输出最小的一个。
样例输入
3
2 5 8
2
10
5
样例输出
8
5
思路:
第一步:三看定乾坤
1.一看题面,就看到了“非降序列”,不用排序,nice。
2.再看题面,0~1000000000(1e9) ,不用开long long。
3.三看题面,1<=n,m<=100000,暴力肯定过不了,要用二分查找法。
第二步:思考——如何二分
第一种方案:
第一种方案,即用二分查找法找到小于等于目标值的最后一个与大于目标值的第一个(或小于目标值的最后一个与大于等于目标值的第一个)。但这种方法需要调试很久,很麻烦,所以我没有用这种方法。
第二种方案:
这种方案可以说是“懒人做法”,即用s数组储存分界点,再用二分查找法查找s数组中的特殊位置,然后做特殊判断,再输出就行了。
代码如下:
#include <bits/stdc++.h>
using namespace std;
int n, m;
int a[100001];
int s[100002];
int main()
{
scanf("%d", &n);
a[0] = 0;
for(int i = 1; i <= n; i++)
{
scanf("%d", a + i);
s[i - 1] = a[i - 1] + a[i]; //储存分界点
s[i - 1] /= 2; //
}
s[0] = -1; //储存特殊分界点
s[n] = 1e9; //
scanf("%d", &m);
while(m--)
{
int x, l, r;
scanf("%d", &x);
l = 0;
r = n;
while(l <= r) //二分查找法 查找特殊位置
{
int mid = (l + r) / 2;
if(x <= s[mid])
r = mid - 1;
else
l = mid + 1;
}
if(l > 1) //特殊判断
{
if(x > s[l - 1] && x <= s[l]) //特殊判断
printf("%d", a[l]);
else
printf("%d", a[l - 1]);
}
else
printf("%d", a[l]);
if(m >= 1)
printf("\n");
}
return 0;
}
注意:
1.第一个特殊判断是必要的,如果没有,会输出很奇怪的数(比如0)。
2.第二个特殊判断是为了确保不会出现特殊情况,但不是必要的。