zoukankan      html  css  js  c++  java
  • 二分答案

    因为期中考试很久没训练 今天是恢复博客更新的第一天 写一写我一直没太整明白的二分答案

    先讲一下什么叫二分答案

    在一个单调区间里查找答案,在正常的思维下都是用暴力枚举。比如说有几个不同大小、已经从小到大排列好的球, 

    它们的直径分别为1,2,3,4,5,10,20(你一开始当然不会知道每一个球的直径),让你从中找出直径为10cm的那一个球。

    一般人都会这样做:从头到尾一个一个的量,直到找到答案为止,这样复杂度最坏情况下为O(n),我们有没有更快的方法呢?

    答案就是用二分,先从区间里找排在中间的元素,找到第四个元素为4,发现4比10小,所以答案应该在第四个元素的后面。然后再从第五位到最后一位

    的区间里找排在中间的元素,找到第六个元素10。OK,问题解决,只用了两次,复杂度为O(log n),优化了很多。

    二分答案,就是用二分的方法,在可能的答案区间里找出问题的答案,大多数情况下用于求解满足某种条件下的最大(小)值,前提是答案具有单调性

    上例题!

    洛谷P1873 砍树

    题目描述

    伐木工人米尔科需要砍倒M米长的木材。这是一个对米尔科来说很容易的工作,因为他有一个漂亮的新伐木机,可以像野火一样砍倒森林。不过,米尔科只被允许砍倒单行树木。

    米尔科的伐木机工作过程如下:米尔科设置一个高度参数H(米),伐木机升起一个巨大的锯片到高度H,并锯掉所有的树比H高的部分(当然,树木不高于H米的部分保持不变)。米尔科就行到树木被锯下的部分。

    例如,如果一行树的高度分别为20,15,10和17,米尔科把锯片升到15米的高度,切割后树木剩下的高度将是15,15,10和15,而米尔科将从第1棵树得到5米,从第4棵树得到2米,共得到7米木材。

    米尔科非常关注生态保护,所以他不会砍掉过多的木材。这正是他为什么尽可能高地设定伐木机锯片的原因。帮助米尔科找到伐木机锯片的最大的整数高度H,使得他能得到木材至少为M米。换句话说,如果再升高1米,则他将得不到M米木材。

    输入输出格式

    输入格式:

    第1行:2个整数N和M,N表示树木的数量(1<=N<=1000000),M表示需要的木材总长度(1<=M<=2000000000)

    第2行:N个整数表示每棵树的高度,值均不超过1000000000。所有木材长度之和大于M,因此必有解。

    输出格式:

    第1行:1个整数,表示砍树的最高高度。

    这个题的答案有明显的单调性,砍树的高度越低,得到的木材就越多,所以用二分在答案区间里找答案就行了。

    上代码

    #include<stdio.h>
    #include<iostream>
    typedef long long ll;
    const int e=(int)1e6+5;
    
    using namespace std;
    ll m,n;
    ll num[e];
    ll mid;
    ll check(ll x)
    {
        ll ans=0;
        for(int i=1;i<=n;i++){
            if(num[i]>x)
            ans+=(num[i]-x);
        }
        return ans;
    }
    
    int main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);
        cin >> n >> m;
        for(int i=1;i<=n;i++){
            cin >> num[i];
        }
        ll r=1000000000;
        ll l=1;
        while(r>=l){
            mid=(r+l)>>1;
           // cout << "*******" << '
    ';
            ll z=check(mid);
            if(z<m)
            r=mid-1;
            else{
                l=mid+1;
            }
        }
        cout << r << '
    ';
        return 0;
    }
    

      

    让我们再来看一题

    P2678 跳石头

    题目背景

    一年一度的“跳石头”比赛又要开始了!

    题目描述

    这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有 NN 块岩石(不含起点和终点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达终点。

    为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走 MM 块岩石(不能移走起点和终点的岩石)。

    输入输出格式

    输入格式:

    第一行包含三个整数 L,N,ML,N,M,分别表示起点到终点的距离,起点和终点之间的岩石数,以及组委会至多移走的岩石数。保证 L geq 1L1 且 N geq M geq 0NM0。

    接下来 NN 行,每行一个整数,第 ii 行的整数 D_i( 0 < D_i < L)Di(0<Di<L), 表示第 ii 块岩石与起点的距离。这些岩石按与起点距离从小到大的顺序给出,且不会有两个岩石出现在同一个位置。

    输出格式:

    一个整数,即最短跳跃距离的最大值。

    上代码

    #include<stdio.h>
    #include<iostream>
    
    using namespace std;
    
    int l,n,m;
    int num[50005];
    
    
    int check(int x)
    {
        int cnt=0;
        int flag=0;
        for(int i=1;i<=n;i++){
            while(num[i]-flag<x&&i<=n)
            i++,cnt++;
            flag=num[i];
        }
        return cnt;
    }
    
    
    int main()
    {        
        ios::sync_with_stdio(false);
        cin >> l >> n >> m;
        for(int i=1;i<=n;i++){
            cin >> num[i];
        } 
        num[++n]=l;
        int left=1 , right=l;
        while(left<=right)
        {
            int mid=(left+right)>>1;
            int z=check(mid);
            if(z>m)
            right=mid-1;
            else{
                left=mid+1;
            }
        }
        cout << right << '
    ';
        return 0;
    }
    人十我百 人百我万
  • 相关阅读:
    网页制作--标签,表格,表单,框架
    sql数据库框架
    数据库的触发器
    数据库的复制与附加
    sql数据库随笔
    sql常用函数
    sql数据类型、约束
    sql数据库查询
    数据库增删改查
    数据库基本概念
  • 原文地址:https://www.cnblogs.com/bestcoder-Yurnero/p/10864171.html
Copyright © 2011-2022 走看看