zoukankan      html  css  js  c++  java
  • 二分题目总结(未完待续)

    二分的用处太大了,不管是求简单的方程,还是求最优解方面都是不错的解题思想。

          只要在线性,顺序或者有序的数据里就可以用二分来找最优的答案,而且时间平均都是O(log2 n)。题目中好像是HDU 4190吧,这题的限时是10000ms,而用二分做才用时1000ms,其优点可想而知。

           不过就像《编程珠玑》中说的一样,虽然二分思路及其做法很爽,但是编写二分的程序总是错漏百出的。二分的第一个程序出现在1942年,但是直到1962年出出现了第一个没有bug的二分程序,其编写正确难度可想而知。

     

    解题技巧:

    (1)整形数据题目:l 为下界,r 为上界

    一般的整形数据的题其循环都是 :

    while ( l < r )   然后l=mid+1,high=mid 这各形式的;

    或者有的题目边界要求比较强就得是  

    while ( l <= r) 然后 l=mid+1,r=mid-1 这各形式;

    还有道题就是CF 371C那道中的边界处理要求比较高就是:

    while(  l+1 < r ) 然后 l=mid,r=mid

    (2)浮点型题目: #define eps 1e-5

    大神说的话:“一般浮点型题目都会与精度打交道,所以势必与eps有关,因为如果如果精度要求0.01,那么如果你在 l=mid+eps这样做的话,这里我设eps为0.00001,那么时间复杂度就会乘以10^3了,那么既然二分是减少时间的,这样又会增加时间复杂 度,那该怎么避免这个problem呢。

    所以在HDU 1551这题上我就掉进了这个坑了,我把精度写在 l=mid+eps里了,然后直接TLE。  我把精度写在while里面的时候时间直接下降很多。因为每次都是平分,这就与eps没多大关系了,只要能接近最优答案就行。所以技巧如下:

    while( r - l >eps)  然后 l=mid , r=mid;即可。”。

    解题的基本思路就是:

    while( l < r )
    {
      mid=(l+r)/2; //如果是整数用移位>>1更加快
      if(gao(mid)<=m) l=mid+1;  //gao函数是处理二分枚举之后验证最佳答案是否符合的函数
      else r=mid; 
    }

    下面是这些题的代码及分析:

     HDU1551    Cable master   http://acm.hdu.edu.cn/showproblem.php?pid=1551

    算是我的二分第一题吧,题意是给你n根绳子,让你分成长度相等m段,求绳子的最长长度。以前根本不知道这种题可以用二分来做,感觉很神奇。

    代码如下:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <math.h>
    #include <algorithm>
    #define eps 1e-8
    using namespace std;
    int n,m;
    double sum;
    double a[10001];
    bool judge(double s)
    {
        int cnt=0;
        for(int i=0; i<n; i++)
        {
            cnt+=(int)(a[i]/s);//这根绳子可以分得段数
        }
        if(cnt>=m) return true;
        else return false;
    }
    int main()
    {
        while(scanf("%d%d",&n,&m)!=EOF&&(n||m))
        {
            sum=0;
            for(int i=0; i<n; i++)
            {
                scanf("%lf",&a[i]);
                sum+=a[i];
            }
            sum/=m;//绳子可以分的最大长度
            double l=0,r=sum;
            while(fabs(r-l)>eps)
            {
                double mid=(l+r)/2.0;
                if(judge(mid))
                    l=mid;
                else r=mid;
            }
            printf("%.2lf
    ",l);
        }
        return 0;
    }

    HDU4109: Distributing Ballot Boxes    http://acm.hdu.edu.cn/showproblem.php?pid=4190

    这题就是上面那题的变形。

    有N个城市,M个投票箱。

    然后是N行,表示每个城市的人口数。

    现在每个城市所有的人要投票,投票箱的大小可以无限大(投票箱全部相同,大小相等),我们现在要求的是最小的投票箱容纳量。

    解题思路:

    如果N == M,则容量肯定为城市人口数最多的那个。

    如果N < M,我们当然要把M全部用光,因为用的箱子越多,平均值越小。这样,我们就可以二分枚举人口数作为投票箱的容量,如果对于当前容量,我们枚举所有城市需要 的箱子数目,小于需要的箱子就说明还可以再小,如果需要的箱子数大于给出的箱子数,说明结果不符。

    PS:

    函数名: ceil
    用 法: double ceil(double x);
    功 能: 返回大于或者等于指定表达式的最小整数
    头文件:math.h
    说明:
    float ceil ( float value )
    返回不小于 value 的下一个整数,value 如果有小数部分则进一位。ceil() 返回的类型仍然是 float,因为 float 值的范围通常比 integer 要大。
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <math.h>
    #include <algorithm>
    #define eps 1e-9//1e-5就WA了
    using namespace std;
    int n,m;
    double maxx,mid,sum;
    double a[500001];
    int judge(int r)
    {
        int cnt=0;
        for(int i=0; i<n; i++)
        {
            if(a[i]<=r)
                cnt++;
            else
            {
                cnt+=(int)(a[i]/r);
                if(a[i]/r-(int)(a[i]/r)>eps)
                    cnt++;
            }
        }
        if(cnt>m)
        {
            return 0;
        }
        return 1;
    }
    int main()
    {
        while(scanf("%d%d",&n,&m)!=EOF&&(n+m)!=-2)
        {
            sum=0;
            maxx=0;
            for(int i=0; i<n; i++)
            {
                scanf("%lf",&a[i]);
                maxx=max(maxx,a[i]);
                sum+=a[i];
            }
            if(n==m)
            {
                printf("%.0lf
    ",maxx);
                continue;
            }
            sum/=m;
            double l=sum,r=maxx;
            while(fabs(r-l)>eps)
            {
                mid=(l+r)/2;
                if(judge(mid)==1)
                {
                    r=mid;
                }
                else
                {
                    l=mid;
                }
            }
            printf("%.0lf
    ",l);
        }
        return 0;
    }

     大神的代码,写的比我的好

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<climits>
    using namespace std;
    
    int a[500010];
    
    int main()
    {
        int city, box, res, r, l, mid;
        int sum;
        bool flag;
        while(scanf("%d%d", &city, &box))
        {
            if(city == - 1 && box == -1)
                break;
            res = 0;
            for(int i = 0; i < city; ++i)
            {
                scanf("%d", &a[i]);
                res = max(res, a[i]);
            }
            if(city == box)
            {
                printf("%d
    ", res);
                continue;
            }
            l = 1, r = res;
            while(l < r)
            {
                flag = true;
                sum = 0;
                mid = (l + r) >> 1;
                for(int i = 0; i < city; ++i)
                {
                    sum += ceil(a[i] * 1.0 / mid); //测试中间值
                    if(sum > box)
                        flag = false;
                }
                if(flag)    r = mid; //中间值可以就减半
                else    l = mid + 1; //不可以则mid+1
            }
            printf("%d
    ", r);
        }
        return 0;
    }
    View Code

    POJ3273: Monthly Expense http://poj.org/problem?id=3273

    题意:就是给出n个数,然后分成m组,要求分得各组的花费之和应该尽可能地小,最后输出各组花费之和中的最大值。

    感觉:二分可以求方程的解,这个我承认。不过这题确实有点神哦……这样也能二分,太神了.

    题意:二分枚举最佳的最大值,然后用最大值去枚举这n个数能分成的组数,逐渐逼近最优答案即可。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define inf 0x3f3f3f3f
    using namespace std;
    int n,m,maxx,a[100005];
    int sum;
    bool judge(int r)
    {
        int cnt=1;
        int count=0;
        for(int i=0; i<n; i++)
        {
            if(count+a[i]<=r)
                count+=a[i];
            else
            {
                cnt++;
                count=a[i];
            }
        }
        if(cnt<=m)
            return true;
        return false;
    }
    int main()
    {
        while(scanf("%d%d",&n,&m)!=EOF)
        {
            sum=0;
            maxx=-1;
            for(int i=0; i<n; i++)
            {
                scanf("%d",&a[i]);
                maxx=max(maxx,a[i]);
                sum+=a[i];
            }
            int l=maxx,r=sum;
            while(l<r)
            {
                int mid=(l+r)/2;
                if(judge(mid))
                {
                    r=mid;
                }
                else l=mid+1;
            }
            cout<<l<<endl;
        }
        return 0;
    }

    sdut2862:勾股定理(二分基础题) http://acm.sdut.edu.cn/sdutoj/problem.php?action=showproblem&problemid=2862

    校赛的题目,当时没做出来,以后只要遇到查找的题目首先就要考虑二分查找,因为普通查找时间复杂度O(n),而二分查找是O(lgn)。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    using namespace std;
    int n;
    long long a[1001];
    bool er(long long a[],int lf,int rf,long long  key)
    {
        int l=lf,mid;
        int r=rf;
        while(l<=r)
        {
            mid = l+(r-l)/2;
            if(a[mid]==key)
                return true;
            else if(a[mid]>key)
            {
                r=mid-1;
            }
            else
            {
                l=mid+1;
            }
        }
        return false;
    }
    int main()
    {
        int T,sum;
        scanf("%d",&T);
        while(T--)
        {
            sum=0;
            scanf("%d",&n);
            for(int i=0; i<n; i++)
            {
                scanf("%lld",&a[i]);
                a[i]=a[i]*a[i];
            }
            sort(a,a+n);
            for(int i=0; i<=n-3; i++)
            {
                for(int j=i+1; j<=n-2; j++)
                {
                    bool t=er(a,j+1,n-1,(a[i]+a[j]));
                    if(t)
                    {
                        sum++;
                    }
                }
            }
            cout<<sum<<endl;
        }
        return 0;
    }
  • 相关阅读:
    Android图片缩放方法
    网站建设底层知识Socket与Http解析
    802.11成帧封装实现(五)
    802.11成帧封装实现(四)
    802.11成帧封装实现(三)
    802.11成帧封装实现(二)
    802.11成帧封装实现(一)
    802.11n协议解析(二)
    802.11n协议解析(一)
    早期主流的wlan技术(二)
  • 原文地址:https://www.cnblogs.com/zhangmingcheng/p/4130682.html
Copyright © 2011-2022 走看看