• [题解]一本通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

  • 相关阅读:
    ASP.NET MVC的过滤器
    EasyUi 合并单元格占列显示
    Ajax异步请求阻塞情况的解决办法
    AutoFac使用总结
    CodeFirst迁移注意点
    DataTable与实体类互相转换
    mysql中注释的添加修改
    MySql修改数据表的基本操作(DDL操作)
    MySql中的约束
    如何使用单例模式返回一个安全的Connection
  • 原文地址:https://www.cnblogs.com/zjd-ac/p/10047353.html
Copyright © 2020-2023  润新知