蓝桥杯冲刺题单--二分

二分

知识点

复制代码
二分:
1.序列二分:在序列中查找(不怎么考,会比较难?)
序列二分应用的序列必须是递增或递减,但可以非严格
只要r是mid-1,就对应mid=(l+r+1)/2
2.答案二分:最值
答案二分更重要的是思路,要自己可以二分的点,一般先写暴力,再将其转为二分。
3.浮点数二分:连续性

1.1序列二分模板题--蓝桥18492

复制代码
模板题目
java 复制代码
package erfen;

import java.util.Scanner;

public class Test1 {
    static int N = 100010;
    static int n, q;
    static int[] a = new int[N];

    //找到等于x的最左边的数的下标
    static int getL(int l, int r, int x) {
        while (l < r) {
            int mid = l + r >> 1;//右移1表示除以2
            if (a[mid] >= x) r = mid;
            else l = mid + 1;
        }
        if (a[l] == x)
            return l;//返回下标
        else
            return -1;//不存在
    }

    //找到等于x的最右边的数的下标
    static int getR(int l, int r, int x) {
        while (l < r) {
            int mid = l + r + 1 >> 1;//右移1表示除以2
            if (a[mid] <= x) l = mid;
            else r = mid - 1;
        }
        if (a[l] == x)
            return l;//返回下标
        else
            return -1;//不存在
    }

    //找到大于等于x的第一个数的下标
    static int lower_bound(int l, int r, int x) {
        while (l < r) {
            int mid = l + r >> 1;//右移1表示除以2
            if (a[mid] >= x) r = mid;
            else l = mid + 1;
        }
        if (a[l] >= x)
            return l;//返回下标
        else
            return -1;//不存在
    }

    //找到大于x的第一个数的下标
    static int upper_bound(int l, int r, int x) {
        while (l < r) {
            int mid = l + r >> 1;//右移1表示除以2
            if (a[mid] > x) r = mid;
            else l = mid + 1;
        }
        if (a[l] > x)
            return l;//返回下标
        else
            return -1;//不存在
    }

    //主逻辑函数
    static void solve() {
        Scanner sc = new Scanner(System.in);

        n = sc.nextInt();
        q = sc.nextInt();
        for (int i = 1; i <= n; i++) {
            a[i] = sc.nextInt();
        }

        for (int i = 0; i < q; i++) {
            int op = sc.nextInt();
            int l = sc.nextInt();
            int r = sc.nextInt();
            int x = sc.nextInt();
            if (op == 1) {
                System.out.println(getL(l, r, x));
            } else if (op == 2) {
                System.out.println(getR(l, r, x));
            } else if (op == 3) {
                System.out.println(lower_bound(l, r, x));
            } else if (op == 4) {
                System.out.println(upper_bound(l, r, x));
            }
        }
    }

    public static void main(String[] args) {
        solve();
    }
}

1.2最大通过数--蓝桥3346--前缀和+二分

复制代码
本题利用了前缀和和二分
思想很重要,首先枚举左边,再在里面枚举右边的通过数。
先写暴力然后就很容易写二分。
注意数值范围

暴力

java 复制代码
package erfen;

import java.util.Scanner;

public class Test2 {
    static int N = 200010;
    static int m,n;
    static long k;
    static long[] a = new long[N];
    static long[] b = new long[N];
    //主逻辑函数
    static void solve(){
        Scanner sc = new Scanner(System.in);

        n = sc.nextInt();
        m = sc.nextInt();
        k = sc.nextLong();
        for (int i = 1; i <= n; i++) {
            a[i] = sc.nextLong();
            a[i] = a[i - 1] + a[i];//计算前缀和
        }
        for (int i = 1; i <= m; i++) {
            b[i] = sc.nextLong();
            b[i] = b[i - 1] + b[i];//计算前缀和
        }
        int ans = 0;
        for (int i = 0; i <= n; i++) {
            //循环左边
            if(a[i] > k) break;//a[0]为0,所以无需担心左边为0没有遍历右边
            long x = k - a[i];//左边可通过i关
            for (int j = 0; j <= m; j++) {
                if(x >= b[j]){//右边可通过j关
                    ans = Math.max(ans, i + j);
                }else{
                    break;
                }
            }
        }

        System.out.println(ans);
    }
    public static void main(String[] args) {
        solve();
    }
}

二分

复制代码
计算前缀和数组,它是递增的,可以应用二分
要二分就要找到单调的趋势,找到可以二分的点。
java 复制代码
package erfen;

import java.util.Scanner;

public class Test2 {
    static int N = 200010;
    static int m,n;
    static long k;
    static long[] a = new long[N];
    static long[] b = new long[N];
    //主逻辑函数
    static void solve(){
        Scanner sc = new Scanner(System.in);

        n = sc.nextInt();
        m = sc.nextInt();
        k = sc.nextLong();
        for (int i = 1; i <= n; i++) {
            a[i] = sc.nextLong();
            a[i] = a[i - 1] + a[i];//计算前缀和
        }
        for (int i = 1; i <= m; i++) {
            b[i] = sc.nextLong();
            b[i] = b[i - 1] + b[i];//计算前缀和
        }
        int ans = 0;
        for (int i = 0; i <= n; i++) {
            //枚举左边
            if(a[i] > k) break;//a[0]为0,所以无需担心左边为0没有遍历右边
            long x = k - a[i];//剩下的
            //二分第二个山洞,找到大于x的第一个数
            //因为是找大于x的第一个数,x可能是最后一个即m,所以r的取值要是m+1
            int l = 0, r = m + 1;
            while(l < r){
                int mid = l + r >> 1;
                if(b[mid] > x) r = mid;
                else l = mid + 1;
            }
            ans = Math.max(ans, i + l - 1);
        }


        System.out.println(ans);
    }
    public static void main(String[] args) {
        solve();
    }
}

1.3答案二分模板--https://hydro.ac/d/shallowdream/p/33

复制代码
求最小的最大值,这题和后面那道数列分段一样,都是要求一个最值。
这个最值是自己进行枚举来得到的,这是一个重要的思路。
第二个就是本题的k次加一操作转换为了元素要达到max需要消耗多少k值,由此找到最小的max

暴力

java 复制代码
package erfen;

import java.util.Scanner;

public class Test3 {
    static int N = 100010;
    static int n;
    static long k;
    static int[] a = new int[N];
    static void solve(){
        Scanner sc = new Scanner(System.in);

        n = sc.nextInt();
        k = sc.nextLong();
        for (int i = 1; i <= n; i++) {
            a[i] = sc.nextInt();
        }

        //枚举最小值
        for (int i = 1; i <= 1e14; i++) {
            long t = k;//每次都要重新初始化
            for (int j = 1; j <= n; j++) {
                //循环数组每个元素看能否达到i
                if(a[j] < i){
                    t = t -(i - a[j]);//a[j]需要(i - a[j])次加一操作才能达到i
                }
                //结束后进行判断
                if(t < 0){
                    //当前数组某个元素不能达到i,那整个数组都不能达到i
                    System.out.println(i - 1);
                    return;//结束全部
                }
            }
        }
    }
    public static void main(String[] args) {
        solve();
    }
}

二分

复制代码
找到可以二分的点:随着枚举的最大的最小值越大,t的值即剩余的k操作越来越少。
java 复制代码
package erfen;

import java.util.Scanner;

public class Test3 {
    static int N = 100010;
    static int n;
    static long k;
    static int[] a = new int[N];
    //
    static boolean check(long m){
        long t = k;//每次都要重新初始化
        for (int j = 1; j <= n; j++) {
            //循环数组每个元素看能否达到m
            if(a[j] < m){
                t = t -(m - a[j]);//a[j]需要(m - a[j])次加一操作才能达到m
            }
            //结束后进行判断
            if(t < 0){
                //当前数组某个元素不能达到i,那整个数组都不能达到i
                /*System.out.println(i - 1);*/
                return false;//结束全部
            }
        }
        //整个循环结束,表示整个数组可以达到m
        return true;
    }
    static void solve(){
        Scanner sc = new Scanner(System.in);

        n = sc.nextInt();
        k = sc.nextLong();
        for (int i = 1; i <= n; i++) {
            a[i] = sc.nextInt();
        }

        //二分最大的最小值
        long l = 1;
        long r = (long)1e14;
        while(l < r){
            long mid = l + r + 1 >> 1;
            if(check(mid)) l = mid;//true,表示可以达到mid,但是这个mid不一定是最大的。所以让l=mid。
            else r = mid - 1;
        }

        System.out.println(l);

    }
    public static void main(String[] args) {
        solve();
    }
}
相关推荐
angushine38 分钟前
Gateway获取下游最终响应码
java·开发语言·gateway
小乐xiaole40 分钟前
蓝桥杯 2025 C++组 省 B 题解
c++·蓝桥杯·深度优先
爱的叹息43 分钟前
关于 JDK 中的 jce.jar 的详解,以及与之功能类似的主流加解密工具的详细对比分析
java·python·jar
一一Null1 小时前
Token安全存储的几种方式
android·java·安全·android studio
AUGENSTERN_dc1 小时前
RaabitMQ 快速入门
java·后端·rabbitmq
晓纪同学1 小时前
C++ Primer (第五版)-第十三章 拷贝控制
java·开发语言·c++
小样vvv1 小时前
【源码】SpringMvc源码分析
java
nzwen6661 小时前
Redis学习笔记及总结
java·redis·学习笔记
燃星cro2 小时前
参照Spring Boot后端框架实现序列化工具类
java·spring boot·后端