欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 财经 > 金融 > 蓝桥杯之二分法

蓝桥杯之二分法

2025/4/19 12:32:21 来源:https://blog.csdn.net/juzihuaile/article/details/147292093  浏览:    关键词:蓝桥杯之二分法

存在某条件使得一边均满足,一边均不满足
如果问题满足某种条件,使得在某个点之前的所有值都满足条件,而之后的所有值都不满足条件(或反之),那么可以使用二分法来找到这个边界。

1.问题的解具有单调性

这是使用二分法的核心条件。单调性可以理解为:

  • 如果某个值 x 满足条件,那么所有小于 x 的值也必然满足条件。
  • 如果某个值 x 不满足条件,那么所有大于 x 的值也必然不满足条件。

 2.问题的典型问法

  • 最大化在满足某条件下的解值:例如“求某个最大值的最小值”。
  • 最小化在满足某条件下的解值:例如“求某个最小值的最大值”。
  • 求某个变量的最大值或最小值:例如“求最小的 x 使得某个条件成立”

3.相关的题目练习

1.分巧克力

问题描述

儿童节那天有 KK 位小朋友到小明家做客。小明拿出了珍藏的巧克力招待小朋友们。

小明一共有 NN 块巧克力,其中第 ii 块是 Hi×WiHi​×Wi 的方格组成的长方形。为了公平起见,

小明需要从这 NN 块巧克力中切出 K 块巧克力分给小朋友们。切出的巧克力需要满足:

  1. 形状是正方形,边长是整数;

  2. 大小相同;

例如一块 6×56×5 的巧克力可以切出 66 块 2×22×2 的巧克力或者 22 块 3×33×3 的巧克力。

当然小朋友们都希望得到的巧克力尽可能大,你能帮小明计算出最大的边长是多少么?

输入描述

第一行包含两个整数 N,KN,K (1≤N,K≤1051≤N,K≤105)。

以下 N 行每行包含两个整数 Hi,WiHi​,Wi​ (1≤Hi,Wi≤1051≤Hi​,Wi​≤105)。

输入保证每位小朋友至少能获得一块 1x1 的巧克力。

输出描述

输出切出的正方形巧克力最大可能的边长。

输入输出样例

例示

输入

2 10
6 5
5 6

输出

2

问题背景

想象一下,你有很多块巧克力,每块巧克力的长和宽都不一样。现在你想要把这些巧克力切成边长为 x 的正方形小块,然后把这些小块拼成一个更大的正方形。问题是:最大的 x 是多少?(也就是说,x 越大越好,但必须保证所有巧克力切出来的小块总数至少有 k 个。)

思路讲解

  1. 什么是二分查找?

    • 二分查找就像在玩“猜数字”的游戏。你心里想一个数字,我猜中间的数字,如果猜大了,你就说“小了”,我就只猜左边的;如果猜小了,你就说“大了”,我就只猜右边的。这样每次都能把范围缩小一半,很快就能找到答案。

  2. 如何用二分查找解决问题?

    • 我们要找的 x 是一个范围,比如从 11000000。我们用二分查找的方法,不断缩小这个范围,直到找到最大的 x

  3. 如何判断一个 x 是否可行?

    • 这里有一个关键函数 check(x),它的作用是判断:如果用边长为 x 的正方形切割所有巧克力块,能否切出至少 k 个小块。

    • 对于每块巧克力,能切出的小块数量是 (长 / x) * (宽 / x)。比如,一块长 5、宽 3 的巧克力,如果 x=2,那么能切出 (5/2)=2 行,(3/2)=1 列,总共 2*1=2 个小块。

    • 把所有巧克力切出来的小块数量加起来,如果总数大于等于 k,说明这个 x 是可行的。

  4. 二分查找的步骤

    • 初始化左右边界:l=1(最小可能的边长),r=1000000(最大可能的边长)。

    • 每次取中间值 mid,看看这个 mid 是否可行:

      • 如果可行,说明可以尝试更大的 x,所以把左边界 l 更新为 mid

      • 如果不可行,说明需要更小的 x,所以把右边界 r 更新为 mid-1

    • 重复这个过程,直到范围缩小到一个值,这个值就是答案。

import java.util.Scanner;public class Main {static int n = 0; // 巧克力块的数量static int k = 0; // 需要切割出的正方形总数static int ans = 0; // 最终答案static final int N = 100020; // 定义数组的最大长度static int[][] a = new int[N][3]; // 存储每个巧克力块的长和宽// 创建判断函数 check,用于判断当前的 x 是否满足条件public static boolean check(int x) {int count = 0; // 记录切割出的正方形总数// 遍历每个巧克力块for (int i = 1; i <= n; i++) {// 每个巧克力块切割出的正方形数量为 (长 / x) * (宽 / x)count += (a[i][1] / x) * (a[i][2] / x);}// 如果总数大于等于 k,说明当前 x 是可行的if (count >= k) {return true;} else {return false;}}public static void main(String[] args) {Scanner sc = new Scanner(System.in);// 输入巧克力块的数量和需要切割出的正方形总数n = sc.nextInt();k = sc.nextInt();// 读取每个巧克力块的长和宽for (int i = 1; i <= n; i++) {a[i][1] = sc.nextInt(); // 长a[i][2] = sc.nextInt(); // 宽}// 初始化二分查找的左右边界int l = 1; // 最小可能的边长int r = 1000000; // 最大可能的边长// 二分查找循环while (r > l) {int mid = (r + l + 1) >> 1; // 计算中间值(右移一位相当于除以 2)// 判断当前 mid 是否满足条件if (check(mid)) {// 如果满足条件,尝试更大的 xl = mid;} else {// 如果不满足条件,尝试更小的 xr = mid - 1;}}// 循环结束时,l 即为满足条件的最大 xans = l;System.out.println(ans); // 输出答案}
}

2.卡牌

问题描述

这天, 小明在整理他的卡牌。

他一共有 nn 种卡牌, 第 ii 种卡牌上印有正整数数 i(i∈[1,n])i(i∈[1,n]), 且第 ii 种卡牌 现有 aiai​ 张。

而如果有 nn 张卡牌, 其中每种卡牌各一张, 那么这 nn 张卡牌可以被称为一 套牌。小明为了凑出尽可能多套牌, 拿出了 mm 张空白牌, 他可以在上面写上数 ii, 将其当做第 ii 种牌来凑出套牌。然而小明觉得手写的牌不太美观, 决定第 ii 种牌最多手写 bibi​ 张。

请问小明最多能凑出多少套牌?

输入格式

输入共 3 行, 第一行为两个正整数 n,mn,m 。

第二行为 nn 个正整数 a1,a2,…,ana1​,a2​,…,an​ 。

第三行为 nn 个正整数 b1,b2,…,bnb1​,b2​,…,bn​ 。

输出格式

一行, 一个整数表示答案。

样例输入

4 5
1 2 3 4
5 5 5 5

样例输出

3

样例说明

这 5 张空白牌中, 拿 2 张写 1 , 拿 1 张写 2 , 这样每种牌的牌数就变为了 3,3,3,43,3,3,4, 可以凑出 3 套牌, 剩下 2 张空白牌不能再帮助小明凑出一套

二分法题型的做题思路

1. 接受数值

首先,我们需要明确题目要求的输入数据是什么。通常,二分法问题会涉及一些数组或数值,我们需要读取这些数据。

2. 定义数组

根据题目需求,定义存储数据的数组或其他数据结构。例如,存储卡牌数量、空白牌数量等。

3. 判断情况

分析题目,确定二分查找的适用场景。二分法通常适用于以下情况:

  • 单调性:问题具有某种单调性,例如数组是有序的,或者某个条件满足时所有左边的值都满足,右边的都不满足。

  • 边界问题:需要找到某个边界值,例如最大值、最小值、第一个满足条件的值等。

4. 定义左右边界

根据问题的范围,初始化二分查找的左右边界:

  • 左边界:通常是问题的最小可能值。

  • 右边界:通常是问题的最大可能值。

5. 创建 check 函数

check 函数是二分法的核心,用于判断某个中间值是否满足条件。这个函数需要根据具体问题来设计。

import java.util.Scanner;public class Main {static long n = 0; // 卡牌种类数static long m = 0; // 空白牌数量static final int N = 10001000; // 定义数组的最大长度static int[] a = new int[N]; // 存储每种卡牌的现有数量static int[] sum = new int[N]; // 存储每种卡牌最多可以补充的空白牌数量// 判断函数,用于判断是否可以凑出 x 套牌public static boolean check(int x) {long v = m; // 初始化可用的空白牌数量for (int i = 1; i <= n; i++) {// 如果现有卡牌数量大于等于 x,说明这种卡牌足够,不需要补充if (a[i] >= x) {continue;}// 如果现有卡牌加上补充的空白牌仍然小于 x,说明无法凑出 x 套牌if (a[i] + sum[i] < x) {return false;}// 计算需要补充的空白牌数量long needed = x - a[i];// 如果可用的空白牌数量不足以补充,返回 falseif (v < needed) {return false;}// 减去已使用的空白牌数量v -= needed;}return true; // 如果所有卡牌都满足条件,返回 true}public static void main(String[] args) {Scanner scan = new Scanner(System.in);// 读取输入数据n = scan.nextLong(); // 卡牌种类数m = scan.nextLong(); // 空白牌数量for (int i = 1; i <= n; i++) {a[i] = scan.nextInt(); // 每种卡牌的现有数量}for (int i = 1; i <= n; i++) {sum[i] = scan.nextInt(); // 每种卡牌最多可以补充的空白牌数量}// 初始化二分查找的左右边界int l = 1; // 最小可能的套牌数int r = 3 * N; // 最大可能的套牌数(一个很大的数)// 二分查找循环while (l < r) {// 计算中间值int mid = (l + r + 1) >> 1;// 判断中间值是否可行if (check(mid)) {// 如果可行,尝试更大的值l = mid;} else {// 如果不可行,尝试更小的值r = mid - 1;}}// 输出结果System.out.println(l); // 最大可以凑出的套牌数scan.close();}
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词