0
点赞
收藏
分享

微信扫一扫

《备战蓝桥》之二分(Java)

诗尚凝寒 2022-03-15 阅读 6

在这里插入图片描述

⭐️前言⭐️
二分的思想就是:
设定左右两个端点,给出判断条件,每次经过判断后都会减少一半数据,最后两个端点无限逼近,夹出符合判断条件的临界答案。


📍内容导读📍

整数二分步骤

  • 找一个区间[L,R],使得答案一定在该区间中
  • 找一个判断条件,使得该判断条件具有二段性,并且答案一定是该二段行的分界点
  • 分析arr[Mid]在该判断条件下是否成立,如果成立,考虑答案在哪个区间;如果不成立,考虑答案在哪个区间
  • 如果更新方式是R=Mid,则不用做任何处理;如果更新方式是L=Mid,则需要在计算Mid是加上1

🍅数的范围

原题链接
💡 题解思路:
该题其实就是一道模板题,按照整数二分的步骤即可解出结果。
数组按序排列,相同数字排列在一起,所有该题所求即为一段x的左右端点,二分输出其两个端点即可。
🔑代码实现:

import java.util.*;
public class Main {
    final static int N=100010;
    static int []arr=new int[N];
    static int n,q;
    public static void main(String[]args) {
        Scanner sca=new Scanner(System.in);
        n=sca.nextInt();
        q=sca.nextInt();
        for(int i=0;i<n;i++)
        arr[i]=sca.nextInt();
        for(int i=0;i<q;i++) {
            int x=sca.nextInt();
            //二分x的左端点
            int l=0,r=n-1;
            //确定区间范围
            while(l<r) {
                int mid=l+r>>1;
                if(arr[mid]>=x)
                r=mid;
                else
                l=mid+1;
            }
            if(arr[l]==x) {
                System.out.print(l+" ");
                r=n-1;
         //二分x的右端点
         //因为前边已经确定了x的左端点,所以二分范围可以从l开始到n
                while(l<r) {
                    int mid=l+r+1>>1;
                    if(arr[mid]<=x)
                    l=mid;
                    else
                    r=mid-1;
                }
                System.out.println(l);
            }else {
                System.out.println(-1+" "+-1);
            }
        }
    }
}

实数二分步骤

细节(只有第四步)与整数二分不同,实数是连续而整数是离散的,所以求mid时一定会取得中间值。
其他步骤与整数二分相同。

🍅数的三次方根

原题链接
💡 题解思路:
1.确定区间:
因为−10000≤n≤10000,所以[L,R]为[-10000,10000].
2.给出判断条件:
midmidmid>=n
3.根据判断情况,确定答案落在哪个区间
4.更新区间范围:
L=mid或者R=mid
在这里插入图片描述

🔑代码实现:

import java.util.*;
public class Main {
    static double n;
    public static void main(String[]args) {
        Scanner sca=new Scanner(System.in);
        n=sca.nextDouble();
        double l=-10000,r=10000;
        while(r-l>1e-8) {
        //因为让保留六位小数,多设置两位小数以精确范围
            double mid=(l+r)/2;
            if(mid*mid*mid>=n)r=mid;
            else
            l=mid;
        }
        System.out.printf("%.6f",l);
    }
}

🍅1.四平方和

原题链接
💡 题解思路:
这道题我们首先想到的是通过暴力循环来求解,分别枚举a,b,c然后求出d即可,但是我们分析时间复杂度,数据范围为5000000,所以每层循环大约次数为2300次,三次循环其时间复杂度将会非常大,所以我们必须通过其他方法来完成。
在一堆数据中寻找符合条件的一组数据,可以通过二分或者哈希两种方法来做(注意:如果通过数组二分也会超时,所以我们把数据存在ArrayList中以减少时间)
🔑代码实现1(链表+二分):
该方法需要注意的是字典序问题,所以需要在list.sort()时用比较器,依次将平方和、c、d的顺序来排序。

import java.util.*;
public class Main {
    static ArrayList<int[]> list = new ArrayList<>();

    public static void main(String[] args) {
        Scanner sca = new Scanner(System.in);
        int n = sca.nextInt();
        for (int c = 0; c * c <= n; c++) {
            for (int d = c; c * c + d * d <= n; d++) {
                int t = c * c + d * d;
                list.add(new int[]{c, d, t});
            }
        }
        list.sort(new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                if (o1[2] != o2[2]) return Integer.compare(o1[2], o2[2]);
                if (o1[0] != o2[0]) return Integer.compare(o1[0], o2[0]);
                return Integer.compare(o1[1], o2[1]);
            }
        });
        for (int a = 0; a * a <= n; a++) {
            for (int b = a; a * a + b * b <= n; b++) {
                int x = n - a * a - b * b;
                int l = 0, r = list.size() - 1;
                while (l < r) {
                    int mid = l + r >> 1;
                    if (list.get(mid)[2] >= x) r = mid;
                    else
                        l = mid + 1;
                }
                if (list.get(l)[2] == x) {
                    System.out.println(a + " " + b + " " + list.get(l)[0] + " " + list.get(l)[1]);
                    return;
                }
            }
        }
    }
}

🔑代码实现2(手动实现哈希表):(手动实现的方法比直接使用哈希表速度快一倍)
(手动实现哈希表就是通过设置两个数组实现一一对应的关系)
哈希表就是一一对应的关系,我们通过升序循环,把第一次出现的平方和所用的c、d存储到数组中,就不用再自行进行排序。

import java.util.Scanner;

public class Main1 {
    final static int N=5000010;
    static boolean[]st=new boolean[N];
    static PII[]p=new PII[N];
    public static void main(String[] args) {
        Scanner sca=new Scanner(System.in);
        int n= sca.nextInt();
        for(int c=0;c*c<=n;c++) {
            for(int d=c;c*c+d*d<=n;d++) {
                int t=c*c+d*d;
                if(!st[t]) {
                    st[t]=true;
                    p[t]=new PII(c,d);
                }
            }
        }
        for(int a=0;a*a<=n;a++) {
            for(int b=a;a*a+b*b<=n;b++) {
                int x=n-a*a-b*b;
                if(st[x]) {
                    System.out.print(a+" "+b+" "+p[x].c+" "+p[x].d);
                    return;
                }
            }
        }
    }
}
class PII {
    int c;
    int d;
    public PII(int c, int d) {
        this.c = c;
        this.d = d;
    }
}

🔑代码实现3(哈希表):

import java.util.*;

public class Main2 {
    static HashMap<Integer,int[]>map=new HashMap<>();
    public static void main(String[] args) {
        Scanner sca=new Scanner(System.in);
        int n=sca.nextInt();
        for(int c=0;c*c<=n;c++) {
            for(int d=c;c*c+d*d<=n;d++) {
                Integer t=c*c+d*d;
                if(map.get(t)==null) {
                    map.put(t,new int[]{c,d});
                }
            }
        }
        for(int a=0;a*a<=n;a++) {
            for(int b=a;a*a+b*b<=n;b++) {
                Integer x=n-a*a-b*b;
                if(map.get(x)!=null) {
                    System.out.println(a+" "+b+" "+map.get(x)[0]+" "+map.get(x)[1]);
                    return;
                }
            }
        }
    }
}

🍅2.机器人跳跃问题

原题链接
💡 题解思路:
这道题我们分析题意可得,每次跳到下一台阶都有可能发生能量变化,但是都是e新=2e-h,我们给定能量区间进行二分,对mid的能量进行判断,如果跳到最后一个台阶且e>=0说明这个初始能量值符合要求。
🔑代码实现:

import java.util.*;
public class Main {
    final static int N=100010;
    static int[]arr=new int[N];
    static int n;
    public static void main(String[]args) {
        Scanner sca=new Scanner(System.in);
        n=sca.nextInt();
        for(int i=0;i<n;i++) {
            arr[i]=sca.nextInt();
        }
        int l = 0, r = 100000;
        while (l < r)
        {
            int mid = l + r >> 1;
            if (check(mid)) r = mid;
            else l = mid + 1;
        }
        System.out.print(l);
    }
    public static boolean check(int e) {
        for(int i=0;i<n;i++) {
            e=2*e-arr[i];
            if(e>=1e5)return true;
 /*如果能量大于最大高度,则后边能量值的变化将为单调递增,
 肯定大于0,所以直接返回true即可*/
            if(e<0)return false;
        }
        return true;
    }
}

🍅3.分巧克力

原题链接
💡 题解思路:
每块巧克力可以分成的小块数等于(h/a)*(w/a) h、w分别为长宽,a为小块巧克力边长,随着边长的增大,可分成的小巧克力块数减少,所以二分边长,判断条件为是否块数大于等于k,如果满足,再把mid调大。
🔑代码实现:

import java.util.*;
public class Main {
    final static int N=100010;
    static int[]h=new int[N];
    static int[]w=new int[N];
    static int n,k;
    public static void main(String[]args) {
        Scanner sca=new Scanner(System.in);
        n=sca.nextInt();
        k=sca.nextInt();
        for(int i=0;i<n;i++) {
            h[i]=sca.nextInt();
            w[i]=sca.nextInt();
        }
        int l=1,r=100000;
        while(l<r) {
            int mid=l+r+1>>1;
            if(check(mid))l=mid;
            else
            r=mid-1;
        }
        System.out.print(l);
    }
    public static boolean check(int a) {
        int res=0;
        for(int i=0;i<n;i++) {
            res+=(h[i]/a)*(w[i]/a);
            if(res>=k)return true;
        }
        return false;
    }
}

⚡️最后的话⚡️

总结不易,希望uu们不要吝啬你们的👍哟(^U^)ノ~YO!!如有问题,欢迎评论区批评指正😁
在这里插入图片描述

举报

相关推荐

0 条评论