[题解]一本通1240:查找最接近的元素

查找最接近的元素

[题目描述]

在一个非降序列中,查找与给定值最接近的元素。

[输入]

第一行包含一个整数n,为非降序列长度。1 ≤ n ≤ 100000。

第二行包含n个整数,为非降序列各元素。所有元素的大小均在0-1,000,000,000之间。

第三行包含一个整数m,为要询问的给定值个数。1 ≤ m ≤ 10000。

接下来m行,每行一个整数,为要询问最接近元素的给定值。所有给定值的大小均在0-1,000,000,000之间。

[输出]

m行,每行一个整数,为最接近相应给定值的元素值,保持输入顺序。若有多个值满足条件,输出最小的一个。

[输入样例]

3

2 5 8

2

10

5

[输出样例]

8

5

[思路]

本题数据比较大,如果枚举最坏情况是m*n=10000*100000=1*10^9,会超时,那么我们就要用到二分。二分的时间复杂度大概是O(m*log(n)),并且二分是建立在有序元素之上的,题目中已经说"为非降序列各元素"所以不用排序,但我当时没有看见所以加了sort,但也没有超时。所以直接套用二分模板得到了代码1:

[代码(60分)]

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<string>
 5 #include<cstring>
 6 #include<algorithm>
 7 using namespace std;
 8 unsigned long long date[100010];
 9 unsigned long long myfind(int left,int right,unsigned long long a){
10     if(left+1==right){
11         return (a-date[left])<(date[right]-a)?date[left]:date[right];/*
12 相当于if((a-date[left])<(date[right]-a)){
13     return date[left];
14 }
15 else {
16     return date[right];
17 }
18 */
19     }
20     int middle=(left+right)/2;
21     if(date[middle]>a){
22         return myfind(left,middle,a);
23     }
24     else {
25         return myfind(middle,right,a);
26     }
27 }
28 int main(){
29     int n;
30     scanf("%d",&n);
31     for(int i=0;i<n;++i){
32         scanf("%u",&date[i]);
33     }
34     sort(date,date+n);//此行可以没有
35     int m;
36     scanf("%d",&m);
37     for(int i=0;i<m;++i){
38         unsigned long long a;
39         scanf("%u",&a);
40         printf("%u
",myfind(0,n,a));
41     }
42     return 0;
43 }
自己手头试了几个数据发现在有两种解是总是输出较大的与题目不符于是把return (a-date[left])<(date[right]-a)?date[left]:date[right];改成了return (a-date[left])<=(date[right]-a)?date[left]:date[right];即如果有相同解返回较小的数。于是产生了代码2:
[代码(70分)]
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<string>
 5 #include<cstring>
 6 #include<algorithm>
 7 using namespace std;
 8 unsigned long long date[100010];
 9 unsigned long long myfind(int left,int right,unsigned long long a){
10     if(left+1==right){
11         return (a-date[left])<=(date[right]-a)?date[left]:date[right];
12     }//<=可以把两个解中取较小值
13     int middle=(left+right)/2;
14     if(date[middle]>=a){
15         return myfind(left,middle,a);
16     }
17     else {
18         return myfind(middle,right,a);
19     }
20 }
21 int main(){
22     int n;
23     scanf("%d",&n);
24     for(int i=0;i<n;++i){
25         scanf("%u",&date[i]);
26     }
27     sort(date,date+n);
28     int m;
29     scanf("%d",&m);
30     for(int i=0;i<m;++i){
31         unsigned long long a;
32         scanf("%u",&a);
33         printf("%u
",myfind(0,n-1,a));
34     }
35     return 0;
36 }
37 /*
38 2
39 6 10
40 1
41 8
42 10
43 */
这就尴尬了,为什么还不对?于是找了个标程对拍(ps:这是我第一次写对拍,只知道原理,没想到很快居然写出来了,第一次写所以文件名都没有太注意(out1是我的,out2是标程,in.in是输入数据。这是关于对拍的链接:对拍)发现:


					
				













可以发现如果要查找的元素比数据中最小的元素还小return (a-date[left])<=(date[right]-a)?date[left]:date[right];就会导致小数减大数又因为是unsigned long long从而导致运算错误。于是加了一个特判如果查找的元素比数据中最小的元素还小返回数据中最小的元素。得到了代码3:

[代码(AC)]
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<string>
 5 #include<cstring>
 6 #include<algorithm>
 7 using namespace std;
 8 unsigned long long date[100010];
 9 unsigned long long myfind(int left,int right,unsigned long long a){
10     if(left+1==right){
11         if(a<date[left]){
12             return date[left];
13         }//特判:防止寻找元素小于最小元素
14         return (a-date[left])<=(date[right]-a)?date[left]:date[right];
15     }
16     int middle=(left+right)/2;
17     if(date[middle]>=a){
18         return myfind(left,middle,a);
19     }
20     else {
21         return myfind(middle,right,a);
22     }
23 }
24 int main(){
25     //freopen("in.in","r",stdin);
26     //freopen("out1.out","w",stdout);
27     int n;
28     scanf("%d",&n);
29     for(int i=0;i<n;++i){
30         scanf("%u",&date[i]);
31     }
32     sort(date,date+n);
33     int m;
34     scanf("%d",&m);
35     for(int i=0;i<m;++i){
36         unsigned long long a;
37         scanf("%u",&a);
38         printf("%u
",myfind(0,n,a));
39     }
40     return 0;
41 }
42 /*
43 10
44 5399
45 14104
46 2296
47 19615
48 29450
49 5146
50 31395
51 5577
52 5189
53 13437
54 10
55 7809
56 26463
57 826
58 27322
59 26548
60 26693
61 2635
62 23163
63 8914
64 19465
65 */

 2018-11-30 23:39:19

原文地址:https://www.cnblogs.com/zjd-ac/p/10047353.html