视频讲解:[GESP202506 五级] 奖品兑换-信息学奥赛GESP等级考试真题解析
一、原题
题目背景
为了保证只有时间复杂度正确的代码能够通过本题,时限下降为 400 毫秒。
题目描述
班主任给上课专心听讲、认真完成作业的同学们分别发放了若干张课堂优秀券和作业优秀券。同学们可以使用这两种券找班主任兑换奖品。具体来说,可以使用 a 张课堂优秀券和 b 张作业优秀券兑换一份奖品,或者使用 b 张课堂优秀券和 a 张作业优秀券兑换一份奖品。
现在小 A 有 n 张课堂优秀券和 m 张作业优秀券,他最多能兑换多少份奖品呢?
输入格式
第一行,两个正整数 n,m,分别表示小 A 持有的课堂优秀券和作业优秀券的数量。
第二行,两个正整数 a,b,表示兑换一份奖品所需的两种券的数量。
输出格式
输出共一行,一个整数,表示最多能兑换的奖品份数。
输入输出样例
输入 #1
bash
8 8
2 1
输出 #1
bash
5
输入 #2
bash
314159 2653589
27 1828
输出 #2
bash
1599
说明/提示
对于 60% 的测试点,保证 1 ≤ a,b ≤ 100,1 ≤ n,m ≤ 500。
对于所有测试点,保证 1 ≤ a,b ≤ ,1 ≤ n,m ≤
。
二、做题思路
1)尝试暴力模拟(考场只能60分)
cpp
#include<bits/stdc++.h>
using namespace std;
int main() {
//确定课堂券n,作业券m
int n,m;
cin>>n>>m;
//确定兑换条件a,b
int a,b;
cin>>a>>b;
//保证a>b
if(a<b) swap(a,b);
//模拟贪心过程
int ans=0;
while(true){
//保证n>m,大的兑换大的
if(n<m) swap(n,m);
n-=a;
m-=b;
//兑换不了,退出
if(n<0||m<=0) break;
//兑换次数+1
ans++;
}
cout<<ans;
}
2)官方二分答案实现
2.1)保证 大对大,小对小

cpp
#include<bits/stdc++.h>
using namespace std;
int n, m, a, b;
int main() {
cin >> n >> m >> a >> b;
if (a > b) swap(a, b);
if (n > m) swap(n, m);
}
2.2)计算二分范围

cpp
int left = min(n / a, m / b);
int right = (n + m) / (a + b);
2.3)二分模板
cpp
int mid = 0, ans;
while (left <= right) {
mid = (left + right) / 2;
if ( is(mid) ) {
left = mid + 1;
ans = mid;
} else {
right = mid - 1;
}
}
cout << ans;
2.4)判断答案标准

cpp
bool is(int x) {
//计算余额
int bank = n - x * a;
int bro = m - x * b;
//计算续弥补的损失
int diffp = b - a;
int money = ceil(-bro * 1.0 / diffp) * diffp;
//无法弥补,直接退出
if ( bank < 0) return false;
if ( bro < 0 && bank < money ) return false;
//能弥补返回true
return true;
}
2.2)保证 大对大,小对小
三、答案
cpp
#include<bits/stdc++.h>
using namespace std;
int n, m, a, b;
bool is(int x) {
//计算余额
int bank = n - x * a;
int bro = m - x * b;
//计算续弥补的损失
int diffp = b - a;
int money = ceil(-bro * 1.0 / diffp) * diffp;
//无法弥补,直接退出
if ( bank < 0) return false;
if ( bro < 0 && bank < money ) return false;
//能弥补返回true
return true;
}
int main() {
cin >> n >> m >> a >> b;
if (a > b) swap(a, b);
if (n > m) swap(n, m);
int left = min(n / a, m / b);
int right = (n + m) / (a + b);
int mid = 0, ans;
while (left <= right) {
mid = (left + right) / 2;
//cout<<mid<<" "<<left<<" "<<right<<endl;
if ( is(mid) ) {
left = mid + 1;
ans = mid;
} else {
right = mid - 1;
}
}
cout << ans;
}
