题目链接: AcWing 1227 分巧克力
题意: 给定若干个矩形,从中切割出不少于$K$个边长相同的正方形,问正方形个数不少于$K$时,边长最大能达到多少
解题思路:
- 显然是一个二分答案的思路,题目中说了每个小朋友至少能分得一个$1\times 1$大小的正方形,所以边长最小为$1$,最大值可以设定一个足够大的数,比如题目中给的数据范围$10^5$,若当前选择的边长满足要求,则说明还有可能取更长的边长,当然也有可能不能取更长了,答案就是当前这个;若当前这个边长不能满足要求,则说明当前边长不可能是最终答案
- 满足要求在此题中的意思是至少能够分得$K$个当前选择边长的正方形
注意事项:
- 首先是选择二分模板,也就是看找要的点是在数轴的左端还是右端,上面分析中,我在此题所选择的性质是能够至少分得$K$个当前$mid$值为边长的正方形,画图如下:
- 如何判断是否满足这个性质?很容易就可以推出,若当前所取的边长为$mid$,对于一个矩形来说,只需要分别用它的长和宽整除这个$mid$,然后把结果相乘,就得到这个矩形能够分成多少个边长为$mid$的正方形了,把所有的矩形分割的结果相加,就得到总数,可以和$K$比较了
代码如下:
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010;
int n, k; // 有n块巧克力,k个小朋友
int wid[N], len[N]; // 分别保存巧克力的长和宽
// 计算当正方形的边长为u时,总共能分得多少块巧克力
int get_sum(int u) {
int sum = 0;
for (int i = 0; i < n; i++) {
// 要的就是整除
sum += ((wid[i] / u) * (len[i] / u));
}
return sum;
}
int main() {
scanf("%d%d", &n, &k);
for (int i = 0; i < n; i++) {
scanf("%d%d", &wid[i], &len[i]);
}
// 二分模板
// 按照y总所分析的,我这里所取的性质是“当边长取mid时,能够分得k块巧克力”
// 显然在一个数轴上,目标值的左侧(比目标值小的数)都是满足这个性质的,右边则不满足
// 所以我们要找的值是满足这个性质的最右端
// 如果当前取的mid满足这个性质,那么l有可能就是我们要找的目标值,所以用l = mid来更新
// 综上所述,用以下模板
int l = 0, r = N;
while (l < r) {
int mid = (l + r + 1) >> 1;
if (get_sum(mid) >= k) {
l = mid;
} else {
r = mid - 1;
}
}
printf("%d\n", l);
return 0;
}
左右边界划分的时候右边界为什么不能是n啊
$n$ 是巧克力块数,跟边长没关系。
okkkkkkkk
老哥,能不能用另一个模板做??
一直WA
对啊对啊我也纳闷这个
check里应该是res < =k 但这样改也是WA
WA
不能,因为这两个二分模板最终落在的边界点,就是满足
check()
条件的。而且你的代码里,你可以思考一下,check()
里面,如果数据中第一个矩形特别小,那么res
在运算第一个矩形的时候的结果也会特别小,就总是会直接返回true
了。是的,我这几天想明白了,感谢!
你硬要用也是可以用的其实,就是找 最小的不符合要求的点 ,这个点减 $1$ ,就是 最大的符合要求的点 了,但在思维上绕了一圈,所以还是建议用另一个模板。
代码如下:
谢谢大佬
不客气hh~~
因为要找的是尽可能大的正方形的边长而不是正方形的数量?
假如当前二分的$x=2$,对于一个面积为$1\times 100$的长方形,如果用面积除以$x^2$的话,那就可以从这个长方形中分割出$25$个小正方形,但实际上这个长方形连一个$2\times 2$的正方形都分割不出来。
为啥不是长方形的面积除以mid^2,而是长和宽分别被mid整除再下取整然后相乘?
不是面积的简单分割,要求得到的巧克力为正方形。
长宽分别除mid后下取整得到的是分别在长宽方向允许切割的次数,再将两个数相乘得到的才是该块巧克力能切出的边长为mid的正方形巧克力个数。
很厉害! 蟹蟹
很棒!
谢谢hh~