• 01:查找最接近的元素


    (http://noi.openjudge.cn/ch0111/01/)

    总时间限制: 
    1000ms
     
    内存限制: 
    65536kB
    描述

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

    输入
    第一行包含一个整数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

    分析:题目可以采用二分算法来解决。
    温馨提示:(1)如果想把程序写会,必须先有正确的思路。可以在纸上手动模拟一下,虽说程序是在计算机上实现,但是在解题的过程中,纸和笔都是必不可少的。
    (2)对于二分算法如何验证程序的正确性:首先验证最大和最小值,然后验证中间的元素;能找到的情况,找不到的情况等等。再次试一下只有一个、二个、3个元素的情况,相等不相等,处于中间的情况等,都尝试一下。

    (3)二分一定要确定:找不到的时候停在那个位置?否则程序好像看起来很对,但往往会出各种意想不到的错误。
    参考程序一:递归版本,[lift,right),边递归边比较,浪费时间版
    #include<iostream>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    int a[100100],minx=2000000000;
    int solve(int left,int right,int x){
        int mid=(left+right)/2;
        if (x==a[mid]) return a[mid];
        if (abs(a[mid]-x)<abs(minx-x)||abs(a[mid]-x)==abs(minx-x)&&a[mid]<minx) minx=a[mid];//每次都和中间值比较,记录符合要求的值,偷懒的办法,其实只需要当只区间剩一个元素,或者没有元素时再比较更好,节省时间。
        if (x<a[mid]&&left<mid) return solve(left,mid,x);
        if (x>a[mid]&&mid+1<right)  return solve(mid+1,right,x) ;
        return minx;
    }
    int main(){
        int n,m;
        cin>>n;
        for(int i=0;i<n;i++) cin>>a[i] ;
        cin>>m;
        for(int i=0;i<m;i++){
            int x;
            cin>>x;
            cout<<solve(0,n,x)<<endl;
        }
        return 0;
    }
    参考程序二:递归版本,[lift,right),边递归边比较,时间版
    #include<cstdio>
    #include<iostream>
    using namespace std;
    int a[100010];
    int n,m;
    int Find(int l,int r,int x){   //[l,r)
        if(l==r)return l;//此时,a[l]<x<a[l+1],没有元素和x相等。
        int mid=(l+r)/2;
        if(a[mid]==x) return mid;//如果找到x直接返回下标
        if(a[mid]>x) return Find(l,mid,x);
        else return Find(mid+1,r,x);
    }
    int main(){
        cin>>n;
        for(int i=0;i<n;i++)cin>>a[i];
        cin>>m;
        for(int i=0;i<m;i++){
            int x;
            cin>>x;
            if(x<=a[0])cout<<a[0]<<endl;//x是最小值,最小值和最大值容易在比较中越界,比如k-1=-1,k=n等,所以单独处理。
            else 
                 if(x>=a[n-1])cout<<a[n-1]<<endl;//x是最大值

    else{ int k=Find(0,n,x);
    //cout<<k<<endl; //比较x-a[k-1]与a[k]-x

    if(x-a[k-1]<=a[k]-x)cout<<a[k-1]<<endl; else cout<<a[k]<<endl; //a[k-1]<=x<=a[k]
    }
    }
    return 0;
    }

    参考程序三:while版本,[lift,right),这个比递归更好一些,节约空间。

    //http://noi.openjudge.cn/ch0111/01/ 
    #include<iostream>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    int n;
    int a[100100],minx=2000000000;
    int solve(int x){//左闭右开 
        if (x<=a[0])return a[0];
        if (x>=a[n-1])return a[n-1];
        int left=0,right=n;
        while (left<right){
            int mid=(left+right)/2;
            if (x==a[mid]) return a[mid];
            if (x<a[mid]) right=mid;
            else left=mid+1;        
            }
        return x-a[left-1]<=a[left]-x?a[left-1]:a[left];
    }
    int main(){
        int m;
        cin>>n;
        for(int i=0;i<n;i++) cin>>a[i] ;
        cin>>m;
        for(int i=0;i<m;i++){
            int x;
            cin>>x;
            cout<<solve(x)<<endl;
        }
        return 0;
    }
    参考程序四:递归1[l,r]
    递归会在" 第一个大于x的位置, 如果多个和x相等,应停留在最左边的位置。"
    #include<cstdio>
    #include<iostream>
    using namespace std;
    int a[100010];
    int n,m;
    int Find(int l,int r,int x){
        //查找满足a[k]>=x最小的位置k 
        if(l==r)return l;
        int mid=(l+r)/2;
        if(a[mid]>=x) return Find(l,mid,x);
        else return Find(mid+1,r,x);
    }
    int main(){
        cin>>n;
        for(int i=0;i<n;i++)cin>>a[i];
        cin>>m;
        for(int i=0;i<m;i++){
            int x;
            cin>>x;
            int k=Find(0,n-1,x);
            //cout<<k<<endl;
            //比较x-a[k-1]与a[k]-x 
            if(k==0)cout<<a[0]<<endl;
            else
                if(x-a[k-1]<=a[k]-x)cout<<a[k-1]<<endl;
                else cout<<a[k]<<endl;
        }
        return 0;
    }
    参考程序五:递归2[l,r]
    数据:
    4
    2 4 6 8
    1
    5
    if(a[mid]>x) return Find(l,mid-1,x)时过不了?
    具体分析:
    L R mid 值
    0 3 1 4
    2 3 2 6
    2 1 1 4
    没有经历l==r,直接就l<r了
    
    
    
    
    #include<cstdio>
    #include<iostream>
    using namespace std;
    int a[100010];
    int n,m;
    int Find(int l,int r,int x){
        //查找满足a[k]>=x最小的位置k 
        if(l==r)return l;
        int mid=(l+r)/2;
        if(a[mid]==x) return mid;
        if(a[mid]>x) return Find(l,mid,x);//这里如果改成【l,mid-1】程序反而不对了,因为mid-1有可能小于l;但是现在mid,mid+1无缝衔接就没有问题了,保留mid就是为了防止找不到时,x掉在空挡里的情况。
        else return Find(mid+1,r,x);
    }
    int main(){
        cin>>n;
        for(int i=0;i<n;i++)cin>>a[i];
        cin>>m;
        for(int i=0;i<m;i++){
            int x;
            cin>>x;
            int k=Find(0,n-1,x);
            //cout<<k<<endl;
            //比较x-a[k-1]与a[k]-x 
            if(k==0)cout<<a[0]<<endl;
            else
                if(x-a[k-1]<=a[k]-x)cout<<a[k-1]<<endl;
                else cout<<a[k]<<endl;
        }
        return 0;
    }

    参考程序六:while循环[lift,right]
    #include<cstdio>
    #include<iostream>
    using namespace std;
    int a[100000+100];
    int n,m;
    int find(int x){
        int l,r;
        l=0;r=n-1;
        while(l<r){
            int mid=(l+r)/2;
            if(a[mid]>=x) r=mid;//找离x最近的那个最小的数 
            else l=mid+1;
        }
        if (l==0) return 0;    //如果找的值是所有数中的最小值,则直接返回最小下标0。
        return x-a[l-1]<=a[l]-x?l-1:l;//循环结束时l=r, 此时a[l]>=x,a[l-1]<x,从两者中找出一个符合题意的 
    }
    int main(){
        cin>>n;
        for(int i=0;i<n;i++) cin>>a[i];
        cin>>m;
        for(int i=0;i<m;i++){
            int x,p;
            cin>>x;
            p=find(x);
            cout<<a[p]<<endl;
        }
        return 0;
    }




  • 相关阅读:
    【响应式Web设计实践 #BOOK#】
    【JS】(+﹏+)~
    -_-#【邮件】qq邮箱不显示图片
    -_-#【Markdown】
    51Nod——N1284 2 3 5 7的倍数
    51Nod——N1118 机器人走方格
    洛谷——P1014 Cantor表
    洛谷—— P1434 滑雪
    洛谷——P1443 马的遍历
    python(24)- 面向对象进阶
  • 原文地址:https://www.cnblogs.com/ssfzmfy/p/7930092.html
Copyright © 2020-2023  润新知