冶炼金属

题目描述

小蓝有一个神奇的炉子用于将普通金属 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≤10。
对于 60% 的评测用例,1≤N≤10。
对于 100% 的评测用例,1≤N≤10,1≤B≤A≤10。

输入样例:

3
75 3
53 2
59 2

输出样例:

20 25

样例解释

当 V=20 时,有:⌊ 75 20 \frac{75}{20} 2075 ⌋=3,⌊ 59 20 \frac{59}{20} 2059 ⌋=2,⌊ 53 20 \frac{53}{20} 2053 ⌋=2,可以看到符合所有冶炼记录。

当 V=25 时,有:⌊ 75 25 \frac{75}{25} 2575 ⌋=3,⌊ 59 25 \frac{59}{25} 2559 ⌋=2,⌊ 53 25 \frac{53}{25} 2553 ⌋=2 ,可以看到符合所有冶炼记录。

且再也找不到比 20 更小或者比 25 更大的符合条件的 V 值了。

二分查找

这段代码的目的是为了解决一个具体问题:在给定一系列冶炼记录的情况下,计算将普通金属O冶炼成特殊金属X的转换率V的可能的最小值和最大值。下面是对代码的详细注释:

#include<bits/stdc++.h>
using namespace std;

// a数组存储每次冶炼投入的普通金属O的数量,b数组存储对应产出的特殊金属X的数量
int a[10010], b[10010];
int n; // 冶炼记录的数量

// 函数check1用于检查给定的转换率mid是否可能是转换率V的最小值
// 即对于所有记录,用a[i]/mid得到的特殊金属X的数量不会大于b[i]
bool check1(int mid) {
    for(int i=0; i<n; i++) {
        if(a[i]/mid > b[i]) return false;
    } 
    return true;
}

// 函数check2用于检查给定的转换率mid是否可能是转换率V的最大值
// 即对于所有记录,用a[i]/mid得到的特殊金属X的数量不会小于b[i]
bool check2(int mid) {
    for(int i=0; i<n; i++) {
        if(a[i]/mid < b[i]) return false;
    } 
    return true;
}

int main() {
    cin >> n;
    int maxn = 0; // 存储所有冶炼记录中投入普通金属O的最大值
    for(int i=0; i<n; i++) {
        cin >> a[i] >> b[i];
        if(a[i] > maxn) maxn = a[i]; // 更新最大值
    }
    // 寻找可能的最小转换率V
    int l=1, r=maxn;
    while(l < r) {
        int mid = l + (r - l) / 2;
        if(check1(mid)) r = mid; // 如果mid作为转换率可能是最小值,则向左(减小mid)搜索
        else            l = mid + 1; // 否则向右(增大mid)搜索
    }
    cout << l << " "; // 输出可能的最小转换率V
    
    // 重置l和r,寻找可能的最大转换率V
    l=1, r=maxn;
    while(l < r) {
        int mid = l + (r - l + 1) / 2;
        if(check2(mid)) l = mid; // 如果mid作为转换率可能是最大值,则向右(增大mid)搜索
        else            r = mid - 1; // 否则向左(减小mid)搜索
    }
    cout << l; // 输出可能的最大转换率V
    return 0;
}

这段代码首先通过输入获取冶炼记录的数量和每条记录的投入与产出量,然后分别定义了两个检查函数check1check2来验证给定的转换率mid是否能满足作为最小值或最大值的条件。最后,利用二分搜索算法分别寻找满足条件的最小和最大转换率,并输出这两个值。

难点解释

bool check1(int mid) {
    for(int i=0; i<n; i++) {
        if(a[i]/mid > b[i]) return false;
    } 
    return true;
}

函数 check1 的目的是确定一个给定的 mid 值是否能作为冶炼转换率 V 的一个可行的最小值。在这个上下文中,mid 是我们正在测试是否作为冶炼转换率的候选数。

在冶炼过程中,普通金属 O 的数量被 V 除以产生特殊金属 X 的数量。在一个有效的冶炼过程中,你不能用比实际更少的 O 产生同样多的 X;换句话说,你不能从不足的 O 中得到过多的 X

这就是 if(a[i]/mid > b[i]) return false; 这行代码的含义。对于每一条记录,如果使用 mid 作为转换率计算出来的 X 的数量(即 a[i]/mid)大于实际产出的 X 的数量(即 b[i]),那么这个 mid 值就过小,不能作为转换率 V 的最小值。

例如,如果你有 75 个 O 和转换率 mid 是 20,那么你将会计算出 75 / 20 = 3X。如果记录显示实际上只产出了 3 个 X,这是合理的,但如果记录显示产出了少于 3 个 X,那么 mid 就不可能是转换率 V 的最小值,因为它给出了一个过高的产出量。

check1 函数检查这个条件是否对于所有记录都是真的。如果是,那么 mid 值至少不会比正确的最小转换率 V 值更小。通过二分搜索算法,我们可以逐步缩小 mid 的范围,直到找到可能的最小转换率 V

04-14 19:49