【ETOJ P1013】小e的书架 题解(二分查找)

136 阅读3分钟

题目描述

小e要把 nn 本规格相同的书放进书架里,对于每一本书,他可以横着放也可以竖着放(不能斜着放)。

书的宽度为 11,高度为 hh,书架的高度为 tt,意味着如果你横着放,每 hh 长度的书架最多叠 tt 本,如果竖着放,每 11 长度的书架可以放一本书(竖着放不能叠起来,且书的高度要小于等于书架高度)。

注意:你不能在横着放的书上面再竖着放,也不能在竖着放的书上方横着放书。

请问书架的至少要多长,才能放下所有的书?

输入格式

第一行一个整数 TT 表示测试样例个数,满足条件:1T1051 ≤ T ≤ 10^5

对于每个样例,一行三个整数表示 nn, hh, tt,满足条件:1n,h,t1071 ≤ n, h, t ≤ 10^7

输出格式

对于每个样例,在一行输出结果(书架的最小长度)后换行。

样例

输入样例1

2
6 4 5
4 3 2

输出样例1

5
6

样例解释

第一个样例,前面 55 本书横着叠放,长度为 44,最后一本书竖着放,总长度为 55

第二个样例,书不能竖着放,必须全部横着放,总长度为 66


思路

在主函数中,首先读取测试用例的数量。对于每个测试用例,再读取三个整数,分别代表书的数量,书的高度,以及书架的高度。

接着,初始化了两个变量,代表二分查找的左右边界。在进行二分查找的过程中,目标是找到一个长度,使得check(mid)函数返回true,且这个长度尽可能小。

check(mid)函数的作用是检查给定的长度是否满足条件。首先,计算如果书横放在书架上,最多可以放多少本书。然后,如果书架的高度小于等于横放书的数量,还会尝试纵放一些书。最后,函数返回的是这个总数是否大于等于书的数量,即是否有足够的空间来放下所有的书。

在主函数的二分查找过程中,每次迭代都会计算中点,然后使用check(mid)函数来检查这个中点是否满足条件。如果满足条件,那么就将右边界移动到这个中点,因为要找到的是满足条件的最小的值;否则,将左边界移动到这个中点。这个过程会一直重复,直到左边界和右边界之间没有其他的值为止。

最后,输出满足条件的最小值,也就是右边界的值,这就是满足条件的最小书架长度。


AC代码

#include <iostream>
#define ll long long
#define AUTHOR "HEX9CF"
using namespace std;

int m;
int n, h, t;

bool check(ll mid) {
	// 横放
	ll cnt = (mid / h) * t;
	if (h <= t) {
		// 竖放
		cnt += mid % h;
	}
	return cnt >= n;
}

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);

	cin >> m;
	for (int i = 1; i <= m; i++) {
		cin >> n >> h >> t;
		ll l = 0;
		ll r = 1e16;
		while (l + 1 < r) {
			ll mid = (l + r) >> 1;
			if (check(mid)) {
				r = mid;
			} else {
				l = mid;
			}
		}
		cout << r << endl;
	}
	return 0;
}