备战蓝桥杯,用JAVA刷洛谷算法题单:【算法1-6】二分查找与二分答案

参考

【算法1-6】二分查找与二分答案 - 题单 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

image-20240306094811081

模板

感谢锋哥给我的模板,不用再被边界和while条件以及最后到底取哪个值发愁了;

  • 添加一个ans变量,l和r初始化为有值的边界下标,while条件为l<=r,最后取得的结果就是ans。
  • 针对题目问题做判断,对于符合条件的要给ans赋值。
  • 例如升序求第一个大于k的那就继续往左找(你想,找到了一个符合这个条件的,但是因为升序,则左边可能还有大于k的),此时r=mid-1

详细代码可见二分查找专题代码.pdf

题目

P2249 【深基13.例1】查找

几个MLE?

package _1_6;

import java.util.Scanner;

public class P2249 {

    private static int n;
    private static int m;
    private static int[] nums;
    private static int[] ks;

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        n = scanner.nextInt();
        m = scanner.nextInt();
        nums = new int[n + 1];
        ks = new int[m];
        for (int i = 1; i <= n; i++) {
            nums[i] = scanner.nextInt();
        }
        for (int i = 0; i < m; i++) {
            ks[i] = scanner.nextInt();
        }
        for (int i = 0; i < m; i++) {
            int res = biSearch(ks[i]);
            System.out.printf("%d ", res);
        }
        scanner.close();
    }

    public static int biSearch(int k) {
        int l = 1, r = n;
        int ans = -1;
        while (l <= r) {
            int m = l + (r - l) / 2;
            if (nums[m] == k) {
                ans = m;
                r = m - 1;
            } else if (nums[m] < k) {
                l = m + 1;
            } else {
                r = m - 1;
            }
        }
        return ans;
    }
}

修改后:使用快速输入输出(别用Scanner和sout了!!!都给我用StreamTokenizer和PrintWriter)看看这前后差距

Java基础之快速输出输入篇_java快速输入-CSDN博客

java快速输入输出 - Kitorio - 博客园 (cnblogs.com)

fc929de58151181250273ecb1c4a16d1

41d88706f564561e634a8d87e1834875

package _1_6;

import java.io.*;

public class P2249 {

    private static int n;
    private static int m;
    private static int[] nums;
    private static int[] ks;

    public static void main(String[] args) throws IOException {
        // algo 快速输入:StreamTokenizer
        StreamTokenizer streamTokenizer = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
        // 注意读之前要nextToken,可以写一个fun
        streamTokenizer.nextToken();
        n = (int) streamTokenizer.nval;
        streamTokenizer.nextToken();
        m = (int) streamTokenizer.nval;
        nums = new int[n + 1];
        ks = new int[m];
        for (int i = 1; i <= n; i++) {
            streamTokenizer.nextToken();
            nums[i] = (int) streamTokenizer.nval;
        }

        for (int i = 0; i < m; i++) {
            streamTokenizer.nextToken();
            ks[i] = (int) streamTokenizer.nval;
        }
        // algo 快速输出:使用sb构造后输出比sout一个个输出占用memory小一点,时间也快
        // StringBuilder stringBuilder = new StringBuilder();
        // for (int i = 0; i < m; i++) {
        //     int res = biSearch(ks[i]);
        //     stringBuilder.append(res).append(" ");
        // }
        // System.out.println(stringBuilder);
        // algo 快速输出:PrintWriter
        PrintWriter printWriter = new PrintWriter(System.out);
        for (int i = 0; i < m; i++) {
            printWriter.printf("%d ", biSearch(ks[i]));
        }
        // 刷新缓冲区(否则输出不了)
        printWriter.flush();
        // 关闭输出流
        printWriter.close();
    }

    public static int biSearch(int k) {
        int l = 1, r = n;
        int ans = -1;
        while (l <= r) {
            int m = l + (r - l) / 2;
            if (nums[m] == k) {
                ans = m;
                r = m - 1;
            } else if (nums[m] < k) {
                l = m + 1;
            } else {
                r = m - 1;
            }
        }
        return ans;
    }
}

P1102 A-B 数对

这道题挣扎了许久,一开始想实现bound,然后狠狠超时

然后用自己的双指针,狠狠WA(目前还不知道为啥,拿不到测试数据)

package _1_6;

import java.io.*;
import java.util.Arrays;

public class P1102 {

    private static StreamTokenizer in;
    private static PrintWriter out;
    private static long[] nums;
    private static long c;
    private static int n;

    public static void main(String[] args) throws IOException {
        in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
        out = new PrintWriter(System.out);

        in.nextToken();
        n = (int) in.nval;
        in.nextToken();
        c = (long) in.nval;
        nums = new long[n];
        for (int i = 0; i < n; i++) {
            in.nextToken();
            nums[i] = (long) in.nval;
        }

        // 先排序
        Arrays.sort(nums);
        // algo 双指针
        long count = 0;
        int l = 0, r = 0;
        for (int i = 0; i < n; i++) {
            // 找到第一个nums[l]-nums[i]>=c,nums[r]-nums[i]>c,则l~r-1区间就是-nums[i]==c
            while (l < n && nums[l] - nums[i] < c) l++;
            while (r < n && nums[r] - nums[i] <= c) r++;
            // 如果l==n则表示找不到对应值
            if (l < n && nums[l] - nums[i] == c && r >= 1 && nums[r - 1] - nums[i] == c)
                count += r - l;
        }
        out.println(count);

        out.flush();
        out.close();
    }

    // WA /(ㄒoㄒ)/~~
    public static void myFun() {
        // 先排序
        Arrays.sort(nums);
        long count = 0;
        int startOfA = 0, endOfA = 0, startOfB = 0, endOfB = 0;
        while (startOfA < n && startOfB < n) {
            // algo 双指针
            // 找到A区间
            while (endOfA + 1 < n && nums[endOfA + 1] == nums[startOfA]) {
                endOfA++;
            }
            // 找到第一个比 nums[a]+c 大或等的 b
            while (startOfB + 1 < n && nums[startOfB] - nums[startOfA] < c) {
                startOfB++;
                endOfB++;
            }
            // b已经是最后一个数的话没必要再找了
            if (startOfB == n - 1) {
                if (nums[startOfB] - nums[startOfA] == c) {
                    count += endOfA - startOfA + 1;
                }
                break;
            }
            // 找到B区间
            while (endOfB + 1 < n && nums[endOfB + 1] == nums[startOfB]) {
                endOfB++;
            }
            // nums[startOfB] - nums[startOfA] == c
            if (nums[startOfB] - nums[startOfA] == c) {
                count += (long) (endOfA - startOfA + 1) * (endOfB - startOfB + 1);
                startOfA = endOfA + 1;
                endOfA = startOfA;
            } else {
                // 此时nums[startOfB] - nums[startOfA] > c
                startOfA = endOfA + 1;
                endOfA = startOfA;
            }
        }
        out.println(count);
    }

}

P1873 [COCI 2011/2012 #5] EKO / 砍树

还是要理解题目找到伐木机锯片的最大的整数高度H,使得他能得到的木材至少为M米。。二分找的是高度,如果得到的木材总数>=m表示至少m米,求最大的H则是找满足这个条件的最大值,所以(总数>=m)符合条件修改ans,且继续往后找(往后就是高度变大,总和变小)

package _1_6;

import java.io.*;
import java.util.Arrays;

public class P1873 {

    private static int n;
    private static int m;
    private static int[] height;

    public static void main(String[] args) throws IOException {
        StreamTokenizer streamTokenizer = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
        PrintWriter printWriter = new PrintWriter(System.out);
        streamTokenizer.nextToken();
        n = (int) streamTokenizer.nval;
        streamTokenizer.nextToken();
        m = (int) streamTokenizer.nval;
        height = new int[n];
        for (int i = 0; i < n; i++) {
            streamTokenizer.nextToken();
            height[i] = (int) streamTokenizer.nval;
        }

        Arrays.sort(height);
        int ans = -1;
        // l r mid 都是高度
        int l = 0, r = height[n - 1];
        while (l <= r) {
            int mid = l + (r - l) / 2;
            long sum = count(mid);
            // 注意条件,应该是要找符合sum>=m的最大的mid
            if (sum >= m) {
                ans = mid;
                l = mid + 1;
            } else {
                r = mid - 1;
            }
        }
        printWriter.println(ans);

        printWriter.flush();
        printWriter.close();
    }

    private static long count(int h) {
        // 从后往前找并计算出砍下来的高度
        long res = 0;
        for (int i = n - 1; i >= 0; i--) {
            if (height[i] <= h) break;
            res += height[i] - h;
        }
        return res;
    }
}

P1024 [NOIP2001 提高组] 一元三次方程求解

数学:盛金公式、卡尔丹公式、牛顿迭代法

我都不用(🤭),直接暴力或者二分

二分50%没用题解那个,两个WA没想到为啥

package _1_6;

import java.io.*;
import java.util.ArrayList;

public class P1024 {

    private static double a;
    private static double b;
    private static double c;
    private static double d;
    private static StreamTokenizer streamTokenizer;
    private static PrintWriter printWriter;
    private static ArrayList<Double> list;

    public static void main(String[] args) throws IOException {
        streamTokenizer = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
        printWriter = new PrintWriter(System.out);

        streamTokenizer.nextToken();
        a = streamTokenizer.nval;
        streamTokenizer.nextToken();
        b = streamTokenizer.nval;
        streamTokenizer.nextToken();
        c = streamTokenizer.nval;
        streamTokenizer.nextToken();
        d = streamTokenizer.nval;

        list = new ArrayList<>();
        for (double i = -100; i <= 99; i++) {
            // 由根与根之差的绝对值>=1得,每个1开区间内都有可能有一个根,通过<0来判断是否有根,有则二分找
            double res1 = fun(i);
            double res2 = fun(i + 1);
            // 这里可能res2==0计入i+1导致下一轮i重复计入,加个查重
            if (!list.contains(i) && res1 == 0) {
                printWriter.printf("%.2f ", i);
                list.add(i);
            }
            if (!list.contains(i + 1) && res2 == 0) {
                printWriter.printf("%.2f ", i + 1);
                list.add(i + 1);
            }
            if (fun(i) * fun(i + 1) < 0) biSearch(i, i + 1);
        }

        printWriter.flush();
        printWriter.close();
    }

    public static void biSearch(double l, double r) {
        // 如果精度过细没必要继续找了
        if (r - l <= 0.001) {
            if (!list.contains(l) && fun(l) == 0) {
                printWriter.printf("%.2f ", l);
                list.add(l);
            }
            return;
        }
        double mid = l + (r - l) / 2;
        // res判断左右两个区间内是否还有解(解在那个区间)
        double res1 = fun(l) * fun(mid);
        double res2 = fun(mid) * fun(r);
        double res = fun(mid);
        if (!list.contains(mid) && res == 0) {
            printWriter.printf("%.2f ", mid);
            list.add(mid);
            return;
        }
        if (res1 < 0) biSearch(l, mid);
        else if (res2 < 0) biSearch(mid, r);
    }

    public static double fun(double t) {
        return Math.pow(t, 3) * a + Math.pow(t, 2) * b + t * c + d;
    }
}

这里double高精度有个注意的点,就是结果和指定值只要差距小于一定值即可认为相等,而不是完全等于

package _1_6;

import java.io.*;
import java.util.ArrayList;

public class P1024 {

    private static double a;
    private static double b;
    private static double c;
    private static double d;
    private static StreamTokenizer streamTokenizer;
    private static PrintWriter printWriter;

    public static void main(String[] args) throws IOException {
        streamTokenizer = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
        printWriter = new PrintWriter(System.out);

        streamTokenizer.nextToken();
        a = streamTokenizer.nval;
        streamTokenizer.nextToken();
        b = streamTokenizer.nval;
        streamTokenizer.nextToken();
        c = streamTokenizer.nval;
        streamTokenizer.nextToken();
        d = streamTokenizer.nval;

        // biFun();

        // 暴力
        for (double i = -100; i <= 100; i += 0.01) {
            // algo 高精度:对于高精度得计算结果,和结果得差小于一定范围内即可
            if (Math.abs(fun(i)) < 0.01) printWriter.printf("%.2f ", i);
        }

        printWriter.flush();
        printWriter.close();
    }

    public static double fun(double t) {
        return Math.pow(t, 3) * a + Math.pow(t, 2) * b + t * c + d;
    }
}
        d = streamTokenizer.nval;

        // biFun();

        // 暴力
        for (double i = -100; i <= 100; i += 0.01) {
            // algo 高精度:对于高精度得计算结果,和结果得差小于一定范围内即可
            if (Math.abs(fun(i)) < 0.01) printWriter.printf("%.2f ", i);
        }

        printWriter.flush();
        printWriter.close();
    }

    public static double fun(double t) {
        return Math.pow(t, 3) * a + Math.pow(t, 2) * b + t * c + d;
    }
}
  • 11
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值