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;
    }
    人十我百 人百我万
  • 相关阅读:
    基于Metaweblog API 接口一键发布到国内外主流博客平台
    uva144 Student Grants
    Uva 10452
    Uva 439 Knight Moves
    Uva 352 The Seasonal War
    switch语句
    java——基础知识
    我的lua学习2
    codeforces 431 D. Random Task 组合数学
    codeforces 285 D. Permutation Sum 状压 dfs打表
  • 原文地址:https://www.cnblogs.com/bestcoder-Yurnero/p/10864171.html
Copyright © 2011-2022 走看看