【洛谷 P9240】[蓝桥杯 2023 省 B] 冶炼金属 题解(二分答案)

112 阅读3分钟

[蓝桥杯 2023 省 B] 冶炼金属

题目描述

小蓝有一个神奇的炉子用于将普通金属 O 冶炼成为一种特殊金属 X。这个炉子有一个称作转换率的属性 VVVV 是一个正整数,这意味着消耗 VV 个普通金属 O 恰好可以冶炼出一个特殊金属 X,当普通金属 O 的数目不足 VV 时,无法继续冶炼。

现在给出了 NN 条冶炼记录,每条记录中包含两个整数 AABB,这表示本次投入了 AA 个普通金属 O,最终冶炼出了 BB 个特殊金属 X。每条记录都是独立的,这意味着上一次没消耗完的普通金属 O 不会累加到下一次的冶炼当中。

根据这 NN 条冶炼记录,请你推测出转换率 VV 的最小值和最大值分别可能是多少,题目保证评测数据不存在无解的情况。

输入格式

第一行一个整数 NN,表示冶炼记录的数目。

接下来输入 NN 行,每行两个整数 A,BA,B,含义如题目所述。

输出格式

输出两个整数,分别表示 VV 可能的最小值和最大值,中间用空格分开。

样例 #1

样例输入 #1

3
75 3
53 2
59 2

样例输出 #1

20 25

提示

【样例说明】

V=20V=20 时,有:7520=3,5320=2,5920=2\left\lfloor\frac{75}{20}\right\rfloor=3,\left\lfloor\frac{53}{20}\right\rfloor=2,\left\lfloor\frac{59}{20}\right\rfloor=2,可以看到符合所有冶炼记录。

V=25V=25 时,有:7525=3,5325=2,5925=2\left\lfloor\frac{75}{25}\right\rfloor=3,\left\lfloor\frac{53}{25}\right\rfloor=2,\left\lfloor\frac{59}{25}\right\rfloor=2,可以看到符合所有冶炼记录。

且再也找不到比 2020 更小或者比 2525 更大的符合条件的 VV 值了。

【评测用例规模与约定】

对于 30%30 \% 的评测用例,1N1021 \leq N \leq 10^{2}

对于 60%60 \% 的评测用例,1N1031 \leq N \leq 10^{3}

对于 100%100 \% 的评测用例,1N1041 \leq N \leq 10^{4}1BA1091 \leq B \leq A \leq 10^{9}

蓝桥杯 2023 省赛 B 组 C 题。


思路

首先,从输入中读取冶炼记录的数量,并存储每条记录的投入普通金属数目和冶炼出的特殊金属数目。

然后,使用二分搜索算法来找出可能的转换率的最大值和最小值。

对于最大值,设定初始搜索范围为0到无穷大。在每次迭代中,计算中间值,然后检查所有记录以确定是否所有的普通金属数量除以这个中间值都大于或等于对应的特殊金属数量。如果是,将搜索范围的下限设为中间值。否则,将搜索范围的上限设为中间值。当搜索范围的上下限差距小于1时,搜索结束,此时的下限即为可能的最大转换率。

对于最小值,设定初始搜索范围为0到无穷大。在每次迭代中,计算中间值,然后检查所有记录以确定是否所有的普通金属数量除以这个中间值都小于或等于对应的特殊金属数量。如果是,将搜索范围的上限设为中间值。否则,将搜索范围的下限设为中间值。当搜索范围的上下限差距小于1时,搜索结束,此时的上限即为可能的最小转换率。

最后,输出可能的最小转换率和最大转换率。


AC代码

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

const int N = 1e5 + 7;
const int INF = 0x3f3f3f3f;

int n;
int a[N], b[N];
int vmin, vmax;

bool check1(int x) {
	// cout << x << "\n";
	for (int i = 1; i <= n; i++) {
		if (a[i] / x != b[i]) {
			return a[i] / x >= b[i];
		}
	}
	return true;
}

bool check2(int x) {
	// cout << x << "\n";
	for (int i = 1; i <= n; i++) {
		if (a[i] / x != b[i]) {
			return a[i] / x <= b[i];
		}
	}
	return true;
}

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

	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> a[i] >> b[i];
	}

	int l, r;
	l = 0;
	r = INF;
	while (l + 1 < r) {
		int mid = (l + r) >> 1;
		if (check1(mid)) {
			l = mid;
		} else {
			r = mid;
		}
	}
	vmax = l;
	// cout << l << " " << r << "\n";

	l = 0;
	r = INF;
	while (l + 1 < r) {
		int mid = (l + r) >> 1;
		if (check2(mid)) {
			r = mid;
		} else {
			l = mid;
		}
	}
	vmin = r;
	// cout << l << " " << r << "\n";

	cout << vmin << " " << vmax << "\n";
	return 0;
}