您现在的位置是:首页 >技术教程 >第十四届蓝桥杯C++ B组——冶炼金属网站首页技术教程

第十四届蓝桥杯C++ B组——冶炼金属

samewide 2023-05-23 16:00:02
简介第十四届蓝桥杯C++ B组——冶炼金属

题目内容

小蓝有一个神奇的炉子用于将普通金属 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 % 30\% 30% 的评测用例, 1 ≤ N ≤ 1 0 2 1≤N≤10^2 1N102
对于 60 % 60\% 60% 的评测用例, 1 ≤ N ≤ 1 0 3 1≤N≤10^3 1N103
对于 100 % 100\% 100% 的评测用例, 1 ≤ N ≤ 1 0 4 , 1 ≤ B ≤ A ≤ 1 0 9 1≤N≤10^4,1≤B≤A≤10^9 1N1041BA109

输入样例:

3
75 3
53 2
59 2

输出样例:

20 25

样例解释
V = 20 V=20 V=20 时,有: ⌊ 75 20 ⌋ = 3 , ⌊ 53 20 ⌋ = 2 , ⌊ 59 20 ⌋ = 2 ⌊frac{75}{20}⌋=3 ,⌊frac{53}{20}⌋=2,⌊frac{59}{20}⌋=2 2075=32053=22059=2,可以看到符合所有冶炼记录。

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

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

二分法

解析

我们可以把 ⌊ A V ⌋ = B ⌊frac{A}{V}⌋ = B VA=B看成一个函数,把 V V V当作自变量, B B B当作因变量,容易知道,这是一个反比例函数的形状,但是由于下取整,所以实际上以一个下降的分段函数
满足条件的v一定是在一段区间上,则满足这一条记录的v的最小值即为这段区间的左端点。分析到这里,这道题目的单调性就被发现了,我们这时采用二分查找即可找到min,再取所有记录的min中最大的一个,即为整体的min
对于max,由于它位于区间的右端点,我们也可以重新写一个二分查找去寻找右端点,但代码量增加,可以采取一个转化,仍然使用同一个二分查找的函数。我们寻找满足B + 1的下一个区间的左端点,此时找到的是满足 ⌊ A V ′ ⌋ = B − 1 ⌊frac{A}{V^prime}⌋ = B - 1 VA=B1,其中可以发现这样的操作会使得 V ′ = V + 1 V^prime = V + 1 V=V+1,因此返回的端点-1即可,最后取所有记录中最小的一个,即为整体的max
请添加图片描述

代码

要注意二分查找中l和r的初始化取值

#include <bits/stdc++.h>

using namespace std;

int binary_search(int a, int b)
{
    int l = 1, r = 2e9 + 1; //由于b存在等于0的情况,也就是说当a = 2e9,b = 0的时候,
    //v必须能取到比a还大的数字,因此要加一
    while(l < r)
    {
        int mid = (l + r) >> 1;
        if(a / mid <= b) r = mid; //这里和正常的二分查找是相反的,因为查找的数字在分母上
        else l = mid + 1;
    }
    return l;
}

int main()
{
    int n;
    cin >> n;
    int v_max = 1e9, v_min = 1; //当A取最大,B取最小的时候可得到最大值1e9,相等的时候取最小值1
    while(n --)
    {
        int a, b;
        scanf("%d %d", &a, &b);
        v_max = min(v_max, binary_search(a, b - 1) - 1); //这里是转化为下一个区间的左端点-1
        v_min = max(v_min, binary_search(a, b));
    }
    
    printf("%d %d", v_min, v_max);
    return 0;
}

公式法

解析

首先我们先列出题目中要求的公式
⌊ A V ⌋ = B ⌊frac{A}{V}⌋ = B VA=B
根据下取整的性质,我们有
⌊ A V ⌋ + 1 = B + 1 > A V ≥ ⌊ A V ⌋ = B ⌊frac{A}{V}⌋ + 1 = B + 1 > frac{A}{V} geq ⌊frac{A}{V}⌋ = B VA+1=B+1>VAVA=B
进行不等式的简单变换得到
A B ≥ V > A B + 1 frac{A}{B} geq V > frac{A}{B + 1} BAV>B+1A
到这里结果就非常明显了,我们只需要寻找 ⌊ A B ⌋ ⌊frac{A}{B}⌋ BA的最小值作为max,由于V是整数,所以min一定是大于 A B + 1 frac{A}{B + 1} B+1A的最小整数,当 A B + 1 frac{A}{B +1} B+1A是整数的时候,直接取 A B + 1 + 1 frac{A}{B+1} + 1 B+1A+1即可,如果不是整数,则取 ⌊ A B + 1 ⌋ + 1 ⌊frac{A}{B + 1}⌋ + 1 B+1A+1,在C++中这两种操作可以合并。

代码

#include <bits/stdc++.h>

using namespace std;

int n;

int main()
{
    cin >> n;
    int maxs = 0x3f3f3f3f, mins = 0;
    for(int i = 0; i < n; i ++)
    {
        int a, b;
        scanf("%d %d", &a, &b);
        maxs = min(maxs, a / b);
        mins = max(mins, a / (b + 1) + 1); //这里是合并的操作
    }
    
    printf("%d %d", mins, maxs);
    return 0;
}
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。