分巧克力问题题解 | 蓝桥杯
📖 题目描述
儿童节当天有 K
位小朋友到小明家做客。小明准备了 N
块巧克力,其中第 i
块为 Hi×Wi
的长方形。需要从这些巧克力中切出 K
块形状相同、大小一致的正方形巧克力,且边长必须为整数。小朋友们希望得到的巧克力尽可能大,求能切出的最大边长。
关键条件
1. 正方形边长为整数
2. 每块巧克力可切割多块小正方形
3. 需满足至少K
块的总需求
🔍 解题思路
🎯 为何选择二分法?
特征 | 满足情况 | 解释 |
---|---|---|
有序区间 | ✔️ 边长区间 [1, max_edge] | 边长越大,能切出的块数越少,解空间具有单调性 |
条件判断函数 | ✔️ check(mid) 快速验证 | 对每个候选边长 mid,可遍历所有巧克力快速计算总块数 |
最优解问题 | ✔️ 寻找最大可能边长 | 通过不断二分区间逼近最大值 |
⚙️ 算法框架
int l = 1, r = max_edge;
while (l < r) {
int mid = (l + r + 1) >> 1; // 向上取整避免死循环
if (check(mid)) l = mid; // 满足条件尝试更大的边长
else r = mid - 1; // 不满足则缩小右边界
}
return l;
⚔️ 算法对比
暴力枚举法
for (int side = MaxSide; side >=1; side--){
// 遍历计算总块数
}
指标 | 说明 |
---|---|
时间复杂度 | O(N*M) → 最坏 1e10 次计算 |
优点 | 逻辑简单 |
缺点 | 无法处理大数据量 |
二分优化法
while (l < r) {
mid = (l + r + 1) >> 1;
if (check(mid)) ...
}
指标 | 说明 |
---|---|
时间复杂度 | O(N*logM) → 约 1e5 次计算 |
优点 | 效率飞跃,可处理1e5量级数据 |
缺点 | 需要理解二分边界条件 |
🚀 核心代码实现
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int h[N], w[N];
int n, k;
bool check(int mid)
{
long long res = 0; // 防止大数溢出,统计巧克力可切割成多少块
for (int i = 0; i < n; ++i)
{
res += (h[i]/mid) * (w[i]/mid);
if (res >= k) return true; // 提前剪枝
}
return false;
}
int main()
{
cin >> n >> k;
for (int i = 0; i < n; ++i)
cin >> h[i] >> w[i];
int l = 1, r = N;
while (l < r)
{
int mid = (l + r + 1) >> 1; // 重要!+1避免死循环
if (check(mid)) l = mid;
else r = mid - 1;
}
cout << l << endl;
return 0;
}
💡 关键技巧
- 防溢出处理:累加时使用
long long
类型存储总块数 - 提前剪枝:在 check 函数中一旦满足条件立即返回,提升效率
- 边界处理:二分时 mid 计算方式
(l + r + 1) >> 1
确保区间正确收敛
🌟 复杂度证明
- 时间复杂度:二分次数为 O(log(max_edge)) ≈ 20次,每次遍历N块巧克力 → O(N logM)
- 空间复杂度:仅需存储巧克力尺寸 → O(N)
实测可在 100ms 内处理 1e5 量级的数据,完美通过蓝桥杯评测要求
```