zoukankan      html  css  js  c++  java
  • 浅谈二分

    这一篇blog说说二分查找

    其实二分我也是初学,也就是前几天才开始读课本,这几天才开始上手打代码,所以我觉得还是有点难度的,

    其实二分粗略的可以理解为一个你npy和你玩的一个游戏,

    让你猜1-1000里面的一个数,你每次告诉他一个数,他告诉你比答案大了还是小了,就很简单。

    但是如果你是从1开始枚举,而她想的是980,那你放心,你还没猜到,你就重返单身贵族了

    所以,我们就要找一种合适的方法来考虑这个问题

    心里的目标	173(1--1000) 
    你的数  关系
      500 	  大了 
      250  大了	
      125  小了
      187  大了
      156  小了
      172  小了
      180 	  大了
      176  大了
      174  大了
      173  bingo! 
    

      文字有点枯燥对不对?

    看一下这个漫画吧 看这

    这样下来 我估计你就会觉得二分稍微简单一点点。

    这个百度百科对于二分查找过程的一个简单介绍,进行二分的前提是必须要保存一个顺序(升序或降序)

    首先,假设表中元素是按升序排列,将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功;否则利用中间位置记录将表分成前、后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表。重复以上过程,直到找到满足条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功。

    折半查找法也称为二分查找法,它充分利用了元素间的次序关系,采用分治策略,可在最坏的情况下用O(log n)完成搜索任务。它的基本思想是:(这里假设数组元素呈升序排列)将n个元素分成个数大致相同的两半,取a[n/2]与欲查找的x作比较,如果x=a[n/2]则找到x,算法终止;如 果x<a[n/2],则我们只要在数组a的左半部继续搜索x;如果x>a[n/2],则我们只要在数组a的右 半部继续搜索x。

    看着是不是有点难受,简单的来说,二分查找就是在过程中,对于你想要的数字的位置 进行一个查找。

    最后对于他的位置进行一个报位。

    什么??? 还没明白??那你现在至少对于二分有一个简单的了解了吧,那就去做几个题吧。

    第一个,

    这次的题目啊,真的水到不行,宁看看这题,没有输出入要求,就一个点,那我直接暴力输出不就行了,来看看

    我一开始的代码啊

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    #define itn int
    using namespace std;
    int main()
    {
        cout<<"1.849016"<<endl;
        return 0;
    }

      (前面的那一堆头文件是新建就有,不是我特意打上的,逃)

    宁看看,满打满算7行就够了,为什么要这么麻烦,还用二分,但是言归正传,我们在水题的时候,当然可以解出方程来,输出。可是如果这个式子再长一点,再难算一点呢?

    所以还是要正儿八经的用二分。(话说我们课上刚刚学了二分法解方程)

    找零点的时候,只用考虑[f(a)*f(b)<0]&&这个函数是连续不断的就可以了

    所以就看一下AC代码吧

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    #define itn int
    #define E 1e-7
    using namespace std;
    double f(double x)
    {
        double y=x*x*x*x*x-15*x*x*x*x+85*x*x*x-225*x*x+274*x-121;
        return y;
    }
    int main()
    {
        double left=1.5,right=2.4;
        while(left+E<right)
        {
            double mid=(left+right)/2.0;
            if(f(mid)>0)
                left=mid;
            else right=mid;
        }
        if(f(left)==0)
            printf("%.6lf ",left);
        else
            printf("%.6lf ",left);
        return 0;
    }

      按要求的六位输出别忘了,

      还有就是千万别忘了开double,不然你出不来这个数

     别问了 问就是抄的之前的代码

    第二个

    题目还是比较好理解的,就是从输入的数据里面找两个值,使得这两个数的和为给定的数M,

    举例来说,就是

    1
    2
    3
    4//输入四个数
    2 5 1 4
    6//最终给定的和

     如果没有解 就输出No!;

    那来看看代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    #define itn int
    using namespace std;
    int a[100000];//定义一个一维数组
    int main()
    {
        int n,m,l,r,mid;//l左指针 r右指针 mid中间数
        cin>>n;
        for(int i=0;i<n;i++)
        cin>>a[i];//输入数据
        cin>>m;//和
        sort(a,a+n);//按从小到大排序
        for(int i=0;i<n;i++)
        {
            l=i;//左
            r=n;//右
            while(l<=r)
            {
                mid = (l+r)/2;
                if(a[i]+a[mid]==m)
                {
                    cout<<a[i]<<" "<<a[mid];//中间取 二分
                    return 0;
                }
                else if(a[mid]+a[i]>m) r=mid-1;//大于就向左取
                else l=mid+1;//小于就向右取
            }
        }
        cout<<"No";//没有结果输出No
        return 0;
    }

      先把这些数字从大到小排列,方便我们的二分,把中间项定义为mid(最左加最右/2)

    然后拿最左与mid来相加与m比较 若是大了就mid向左指一个,反之向右指一个。

    其中有一个代码,我一开始写的时候没有加上

    1
    2
    l=i;//左
            r=n;//右

      没有明白为什么要这么做

    于是第一次交的时候就GG了

    后来加上了就AC了。

    所以二分一定要记得定义指针啊(这不是指针,但是作用差不多,别杠),不要忘记!!!

    第三个是来自于洛谷的题目

     

     
    #include<iostream>
    #include<cstdio>
    long long m,n,a[1000005],temp;//因为我菜,所以就全设成long long;
    using namespace std;
    long long check(long long x)//这个check函数是二分的最重要的一环
    {
        long long ans=0;
        for(int i=1;i<=n;i++)
        {
            if(a[i]>x)
            ans=ans-x+a[i];//ans用来记录能够得到的木材长度
         } 
         return ans;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            temp=max(temp,a[i]);//temp用来记录最高的树的高度
        }
        long long l=1,r=temp;//把右边界设成最高的树的高度
        while(l<=r)//二分操作
        {
            long long mid=(l+r)>>1,q=check(mid);
            if(q<m)r=mid-1;
            else l=mid+1;
        }
        printf("%d",r);
        return 0;
    }
    

      不解释了自己看吧

    其实二分会在很多高难度题目里面出现,所以二分还是一个必不可少的算法,所以尝试去理解,理解完可以去洛谷或者一本通题库里面去a题尝试一下

    (我是初学,所以如果有错,请各位大佬指出 谢谢)

     
  • 相关阅读:
    系统消息系统公告数据建模
    Chrome部分站点无法启用Flash问题
    使用netstat命令查看端口的使用情况
    Jave基本数据类型
    Mac下Tomcat安装&配置&80默认端口设置
    React学习及实例开发(一)——开始(转载)
    jQuery、layer实现弹出层的打开、关闭功能实例详解
    jQuery获取节点和子节点文本的方法
    jquery 获取$("#id").text()里面的值 需要进行去空格去换行符操作
    ES6数组新增的几个方法
  • 原文地址:https://www.cnblogs.com/--840-114/p/12966841.html
Copyright © 2011-2022 走看看