题目
4956. 冶炼金属 - AcWing题库
蓝桥杯2023年第十四届省赛真题-冶炼金属 - C语言网
小蓝有一个神奇的炉子用于将普通金属 O 冶炼成为一种特殊金属 X 。
这个炉子有一个称作转换率的属性 V , V 是一个正整数,这意味着消耗 V 个普通金属 O 恰好可以冶炼出一个特殊金属 X ,当普通金属 O 的数目不足 V 时,无法继续冶炼。
现在给出了 N 条冶炼记录,每条记录中包含两个整数 A 和 B ,这表示本次投入了 A 个普通金属 O ,最终冶炼出了 B 个特殊金属 X 。
每条记录都是独立的,这意味着上一次没消耗完的普通金属 O 不会累加到下一次的冶炼当中。
根据这 N 条冶炼记录,请你推测出转换率 V 的最小值和最大值分别可能是多少,题目保证评测数据不存在无解的情况。
输入格式
第一行一个整数 N ,表示冶炼记录的数目。
接下来输入 N 行,每行两个整数 A、B ,含义如题目所述。
输出格式
输出两个整数,分别表示 V 可能的最小值和最大值,中间用空格分开。
数据范围
对于 30% 的评测用例, 1≤N≤102 。
对于 60% 的评测用例, 1≤N≤103 。
对于 100% 的评测用例, 1≤N≤104 , 1≤B≤A≤109 。
输入样例:
3
75 3
53 2
59 2
输出样例:
20 25
样例解释
当 V=20 时,有: ⌊2075⌋=3 , ⌊2053⌋=2 , ⌊2059⌋=2 ,可以看到符合所有冶炼记录。
当 V=25 时,有: ⌊2575⌋=3 , ⌊2553⌋=2 , ⌊2559⌋=2 ,可以看到符合所有冶炼记录。
且再也找不到比 20 更小或者比 25 更大的符合条件的 V 值了。
解题
方法一:二分查找
思路
由题意可知,我们需要找出 Vmin 与 Vmax ,使得 ⌊VAi⌋=Bi (其中 i=1…N,V∈[Vmin,Vmax] )。
由于每条冶炼记录都是独立的,要使所有冶炼记录都成立, V 的范围就应是所有 Vi 的交集,也就是说: Vmin=max(V1min,V2min,…,VNmin),Vmax=min(V1max,V2max,…,VNmax) 。
对于第 i 条冶炼记录,ViAi 是单调的( Vi↑,ViAi↓ ),所以 Vi 的上界与下界可以借助二分查找找出来。
代码
#include <iostream>
using namespace std;
int n, a, b;
int main() {
scanf("%d", &n);
int vmn = 1, vmx = 2e9;
while (n--) {
scanf("%d%d", &a, &b);
int l = 1, r = 2e9;
while (l < r) {
int m = l + (r - l >> 1);
if (a / m <= b) r = m;
else l = m + 1;
}
vmn = max(vmn, l);
l = 1, r = 2e9;
while (l < r) {
int m = l + (r - l + 1 >> 1);
if (a / m >= b) l = m;
else r = m - 1;
}
vmx = min(vmx, r);
}
printf("%d %d\n", vmn, vmx);
return 0;
}
方法二:数学
思路
Vi 的上界和下界也可以通过公式直接算出来。
⌊ViAi⌋=Bi⟹ViAi∈[Bi,Bi+1)⟹Bi≤ViAi<Bi+1⟹Bi+1Ai<Vi≤BiAi⟹Vimax=⌊BiAi⌋,Vimin={Bi+1Ai+1⌈Bi+1Ai⌉Bi+1Ai∈ZBi+1Ai∈/Z⟹Vimax=⌊BiAi⌋,Vimin=⌊Bi+1Ai⌋+1
代码
#include <iostream>
using namespace std;
int n, a, b;
int main() {
scanf("%d", &n);
int vmn = 1, vmx = 2e9;
while (n--) {
scanf("%d%d", &a, &b);
vmn = max(vmn, a / (b + 1) + 1);
vmx = min(vmx, a / b);
}
printf("%d %d\n", vmn, vmx);
return 0;
}
评论区