*个人学习及思路记录,含个人浅薄理解,可供参考。
题目:
蓝桥杯2023年第十四届省赛真题-冶炼金属 - C语言网 (dotcpp.com)
题目分析:
- 关键词:转化率V,原料数A,产物数B,N组独立数据;
- 题目翻译:对于单组数据:
,可得整数Vi取值范围[Vimin,Vimax]。
对N组Vi取交集得到合法V,即合法V对所有A与B应满足:,V取值范围[Vmin,Vmax]。Vmin、Vmax即为题目所求。
- PS:除法默认向下取整(结果是某一整数定值),但本题中V明显是要取一整数范围的未知数,因此
这里不能简单等价于Vi = Bi/Ai,正确思路后面有详细解析。
- 转编程思维:
(暴力法):按上述翻译,对所有求得的Vmin、Vmax求交集(因为题目保证有解,所以求max{Vimin}、min{Vimax})。
二分法思路:
“合法V对所有A与B应满足:,V取值范围[Vmin,Vmax]”
《==》对所有合法V,满足(此处除法不取整)
《==》对所有合法V,满足(此处除法不取整)
《==》对[Vmin,Vmax],一定满足(此处除法取整)
******注意左边界是而不是
*******
得到如下区间图:
问题拆解与具体分析:
- 求Vmin:
对V<Vmin,由交集思维,至少有一组数据不满足,即至少有一组数据满足
(以下除法均取整);
对V>=Vmin,所有V满足。
由此得出循环终止条件与if-else分类标准:while (lmin < rmin) { for (int i = 0; i < n; i++) { if (mid < a[i]/(b[i]+1)+1) { lmin = mid + 1; flag = 1; //标记,该mid处于min左侧 break; } } if (!flag) { //mid处于min右侧,则执行 rmin = mid; } mid = lmin + rmin >> 1; flag = 0; //刷新flag } flag = 0; }
- 求Vmax同理。
while (lmax < rmax) { for (int i = 0; i < n; i++) { if (mid > a[i]/b[i]) { rmax = mid -1; flag = 1; //标记,该mid处于max右侧 break; } } if (!flag) { //mid处于max左侧,则执行 lmax = mid; } mid = lmax + rmax + 1>> 1; flag = 0; }
tips:
是否进行+-1修正和不等式是否取等同步(恰好取等时不能进行修正);
修正时Vmin可由小趋大、Vmax可由大趋小,因此求Vmax的时候mid = lmax + rmax + 1>> 1,否则可能会出现某一步lmax正好取max-1而无法进行修正、进入死循环的情况。
完整代码:
/*
小蓝有一个神奇的炉子用于将普通金属 O 冶炼成为一种特殊金属 X。
这个炉子有一个称作转换率的属性 V,V 是一个正整数,这意味着消耗 V 个普通金属 O
恰好可以冶炼出一个特殊金属 X,当普通金属 O 的数目不足 V 时,无法继续冶炼。
现在给出了 N 条冶炼记录,每条记录中包含两个整数 A 和 B,这表示本次投入了
A 个普通金属 O,最终冶炼出了 B 个特殊金属 X。每条记录都是独立的,
这意味着上一次没消耗完的普通金属 O 不会累加到下一次的冶炼当中。
根据这 N 条冶炼记录,请你推测出转换率 V 的最小值和最大值分别可能是多少,
题目保证评测数据不存在无解的情况。
*/
//#include <bits/stdc++.h>
#include<iostream>
#include<vector>
#define ll long long
#define endl '\n'
using namespace std;
void solve() {
int n;
cin >> n;
vector<ll>a(n), b(n);
for (int i = 0; i < n; i++) {
cin >> a[i] >> b[i];
}
// 输入
ll lmin = 1; ll rmin = 1e9;
ll mid = lmin + rmin >> 1;
int flag = 0;
while (lmin < rmin) {
for (int i = 0; i < n; i++) {
if (mid < a[i]/(b[i]+1)+1) {
lmin = mid + 1;
flag = 1; //标记,该mid处于min左侧
break;
}
}
if (!flag) {
//mid处于min右侧,则执行
rmin = mid;
}
mid = lmin + rmin >> 1;
flag = 0; //刷新flag
}
flag = 0;
// 处理min
ll lmax = 1; ll rmax = 1e9;
mid = lmax + rmax + 1>> 1;
while (lmax < rmax) {
for (int i = 0; i < n; i++) {
if (mid > a[i]/b[i]) {
rmax = mid -1;
flag = 1; //标记,该mid处于max右侧
break;
}
}
if (!flag) {
//mid处于max左侧,则执行
lmax = mid;
}
mid = lmax + rmax + 1>> 1;
flag = 0;
}
// 处理max
cout << lmin << ' '<< lmax;
// 输出
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
solve();
return 0;
}
吐槽:蓝桥杯真题初尝试,每一条说明背后都是一个坑......明明是简单的一道题但感觉自己把所有的坑全栽了一遍......