二分法实例
分巧克力
题目描述
儿童节那天有 K 位小朋友到小明家做客。小明拿出了珍藏的巧克力招待小朋友们。小明一共有 N 块巧克力,其中第 i 块是 Hi×Wi 的方格组成的长方形。为了公平起见,小明需要从这 N 块巧克力中切出 K 块巧克力分给小朋友们。切出的巧克力需要满足:
形状是正方形,边长是整数;
大小相同;
例如一块 6x5 的巧克力可以切出 6 块 2x2 的巧克力或者 2 块 3x3 的巧克力。
当然小朋友们都希望得到的巧克力尽可能大,你能帮小明计算出最大的边长是多少 ?
输入描述
第一行包含两个整数 N,K (1 <= N, K <=10^5 )。
以下 N 行每行包含两个整数 Hi,Wi (1 <= Hi,Wi <=10^5 )
输入保证每位小朋友至少能获得一块 1x1 的巧克力。
输出描述
输出切出的正方形巧克力最大可能的边长。
输入样例:
2 10
6 5
5 6
输出:2
分析:
用二分查找为判定的前提条件是答案具有单调性,很显然本题巧克力的边长具有单调性,故可以用二分查找解法。
思路:
这道题考察的其实是对图形分割的一种理解,题目要求的是分割成等面积的正方形,那么我们可以知道,一个长为m,宽为n的长方形能分割为宽度为width的正方形的个数:num=(m/width)*(n/width),利用二分查找1到100000中个数大于K中width最大的值输出即可。
package 分巧克力;
import java.util.Scanner;
//从这 n 块巧克力中切出k块大小相同,形状是正方形巧克力
public class Main {
static int n;
static int k;//人数
static int length[] = new int [100005];
static int width [] = new int [100005];
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
k = sc.nextInt();
for (int i = 0; i < n; i++) {//完成对各个巧克力的长和宽的初始化
length[i] = sc.nextInt();
width[i] = sc.nextInt();
}
//二分查找
int low = 1, high = 100000;
int mid = (low + high) /2;
while(low <=high) {
mid = (low + high) /2;
if(judge(mid)) {
low = mid+1;//如果mid是符合条件的宽度,则可以令下限加1,寻找符合条件的更大的边长
}
else high = mid-1 ;
}
System.out.println(mid);
}
public static boolean judge(int w) {//判断函数,判断宽度为width时切分的个数是否大于人数
int cnt = 0;
for (int i = 0; i < n; i++) {
cnt += (length[i] / w) * (width[i] / w);
}
if(cnt >= k)
return true;
else
return false;
}
// int[3][4]; 3个一维数组,每一个一维数组当中有4个元素 a[二维数组中一维数组下标][一维数组中的下标]
}
分析题目:
这K块巧克力都是正方形而且大小都是一样的, 希望找到边长尽可能大的巧克力使得切出来的个数满足K个小朋友的需求。题目已知最大的矩形边长为100000,最小边长为1我们的目标是在这个范围内找到一个边长最大为t的正方形,使得切出来的个数是大于或等于K,分析到这里应该可以想到二分查找了吧。二分查找解决的题目一般是已知一个范围,求解的问题是一般是从这个范围中找出满足题目要求的最佳值
二分查找一般都是可以使用模板解决的,首先在循环中求解出左右边界[l, r]的中间范围,通过一个check函数传递这个中间范围结合题目的要求判断当前的mid是否满足题目的条件,如果满足那么就更新左边界或者是右边界,在check函数中满足的条件下可以使用一个变量r来记录满足题目要求的中间值(使用变量记录的目的是为了避免最后左右边界上的处理导致的错误),最后根据题目的要求输出或者返回r 变量值即可。对于这道题目来说我们需要在最小范围1与最大范围100000中找到一个最大的边长mid,使用check函数判断当前的边长mid是否满足切出来的数目大于等于k的条件
M 次方根
打算设计一个程序用于求解 M 次跟下N的值。
但是由于要考虑精度范围,答案必须要保留 7 位小数,连三次根号下 27 都要掰手指的小 A 又怎么会设计呢。请你帮小 A 设计一个程序用于求解 M 次根号 N。
数据范围:
1<= N <= 1e5
1<= M <= 100
且 M<N
输入描述: 第一行输入整数 N 和 M,数据间用空格隔开。
输出描述: 输出一个整数,并保留 7 位小数。
输入样例:
27 3
输出样例:
3.000000
分析题目:
前面讲的都是整数二分,其实二分法还是可以用于实数;要找到一个具有单调性的数列,去二分。这个题的关键是我们要去二分什么,这里可以二分的是 a^M=N 中的 a,N所以我们要先想办法设计出用于处理实数二分的代码。
以下是一个模板:
实数域二分,设置eps法:
//令eps 为小于题目精度一个数即可。
//比如题目说保留4位小数,0.0001 这种的。那么eps 就可以设置为五位小数的任意一个数 0.00001 到 0.00009 都可以。一般为了保证精度,我们也会选取精度 /100 的那个小数,即设置 eps= 0.0001/100 = 1e-6。(表示1乘以10的负6次方)
while (l + eps < r) {
double mid = (l + r) / 2;
if (pd(mid))
r = mid;
else
l = mid;
}
关于判定条件,我们应该设计一个代码用于比较 a^M 和 N 的大小关系。
pd 成功的情况,一定是 pd 的 mid 符合条件,且小于 mid 的一定符合条件。因此我们要在大于 mid 中继续查找,找到更大的 mid。
double pd(double a,int m)// a^m=n 判断a是否符合
{
double c=1;
while(m>0)//此处实现a的m次方
{
c=c*a;
m--;
}//可以利用2^3=8 a=2 m=3 那最后c=8 那n=7
if(c>=n)
return true;
else
return false;
}
代码实现
package 二分法样例;
/*求解 M 次根号 N*/
import java.util.Scanner;
public class FangGen {
static double n, l, r, mid;
static double eps = 1e-8;//为了保证精度
static boolean pd(double a, int m) {
double c = 1;
while (m > 0) {
c = c * a;
m--;
}
if (c >= n)
return true;
else
return false;
}
public static void main(String[] args) {
int m;
Scanner in = new Scanner(System.in);
n = in.nextDouble();
m = in.nextInt();
// 设置二分边界(指的是a^m=n 中的a的取值范围),二分的是 a^M=N 中的 a
l = 0;
r = n;
// 实数二分
while (l + eps < r) {
double mid = (l + r) / 2;
if (pd(mid, m))//pd 成功的情况,一定是 pd 的 mid 符合条件,且小于 mid 的一定符合条件。因此我们要在大于 mid 中继续查找,找到更大的 mid。
r = mid;
else
l = mid;
}
System.out.println(String.format("%.7f", l));
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!