分巧克力
题目描述
儿童节那天有 K 位小朋友到小明家做客。
小明拿出了珍藏的巧克力招待小朋友们。
小明一共有 N 块巧克力,其中第 i 块是 Hi×Wi 的方格组成的长方形。
为了公平起见,小明需要从这 N 块巧克力中切出 K 块巧克力分给小朋友们。
切出的巧克力需要满足:
- 形状是正方形,边长是整数
- 大小相同
例如一块 6×5 的巧克力可以切出 6 块 2×2 的巧克力或者 2块 3×3 的巧克力。
当然小朋友们都希望得到的巧克力尽可能大,你能帮小明计算出最大的边长是多少么?
输入格式
第一行包含两个整数 NN 和 KK。
以下 NN 行每行包含两个整数 HiHi 和 WiWi。
输入保证每位小朋友至少能获得一块 1×11×1 的巧克力。
输出格式
输出切出的正方形巧克力最大可能的边长。
数据范围
1≤N,K≤105
1≤Hi,Wi≤105
输入样例:
2 10
6 5
5 6
输出样例:
2
思路分析
此题,无非是把一位的问题扩展到所谓的二维问题,其实题目中条件说,必须分出来的巧克力,是正方型,也就表明我们可以枚举边的长度,在计算出一块巧克力能分出多少块这样的正方型。
重点: 如何计算长方形巧克力,能分出多少块这样边长的巧克力?
- 推理可知,必须当 正方形的边长小于 长方形中边最小的一边才能成功分出对应大小的正方形巧克力,也就是满足条件 w < = m i n ( H , W ) w<=min(H,W) w<=min(H,W).
- 所以我们可以通过公式: ( H [ i ] / w ) ∗ ( W [ i ] ) / w (H[i]/w) * (W[i])/w (H[i]/w)∗(W[i])/w 计算出第 i 块巧克力能分成边长是 w 的巧克力数量。
注意:中间数m所属于的区间范围,来选择二分模板。
解题代码
Java
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner cin = new Scanner(System.in);
int n = cin.nextInt();
int k = cin.nextInt();
int[] H = new int[n];
int[] W = new int[n];
for (int i = 0; i < n; ++ i) {
H[i] = cin.nextInt();
W[i] = cin.nextInt();
}
int ans = breash(H, W, k);
System.out.println(ans);
}
public static boolean check(int m, int[] H, int[] W, int k) {
int sum = 0;
for (int i = 0; i < H.length; ++ i) {
sum += (H[i]/m) * (W[i]/m); // 计算第i块巧克力能分成几块m边长的正方形
if (sum >= k) return true;
}
return false;
}
public static int breash(int[] H, int[] W, int k) {
int l = 1;
int r = 100000;
while (l < r) {
int m = (l + r + 1) >> 1;
if (check(m, H, W, k)) l = m;
else r = m - 1;
}
return r;
}
}
C++
#include <iostream>
using namespace std;
const int N = 1e5 + 5;
int h[N], w[N];
int n, k;
bool check(int m) {
int sum = 0;
for (int i = 0; i < n; ++ i) {
sum += (h[i]/m)*(w[i]/m);
if (sum >= k) return true;
}
return false;
}
int breash() {
int l = 1;
int r = 1e5;
while (l < r) {
int m = (l + r + 1) >> 1;
if (check(m)) l = m;
else r = m - 1;
}
return l;
}
int main() {
cin >> n >> k;
for (int i = 0; i < n; ++ i) {
cin >> h[i] >> w[i];
}
int ans = breash();
cout << ans << endl;
return 0;
}