[GESP202506 五级] 奖品兑换

视频讲解: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;	
}
相关推荐
ZhengEnCi3 小时前
模型训练完整流程:从数据准备到模型上线的 10 个关键步骤(全文可跑)
后端·算法
吞下星星的少年·-·7 小时前
Div.3 CF 1131A题(排序,模拟)
算法·模拟·排序
aichitang20247 小时前
代数拓扑学与现代前沿视角
考研·算法·数学建模·ai·拓扑学·高考
二本咕咕-机械转码8 小时前
FreeRTOS深入解析(一)_链表与并发保护——哨兵循环链表、排序键游标、临界区与关调度
单片机·算法·链表
-银雾鸢尾-8 小时前
两数之和-C++实现
c++·算法
j7~8 小时前
【动态规划算法专题】动态规划五部曲实战:泰波那契数、三步问题、最小花费爬楼梯
c++·算法·动态规划·数据结构与算法
倍利福猎头公司官方账号8 小时前
大企业HR如何向猎头公司清晰传达AI岗位的技术需求?
人工智能·算法·机器人·求职招聘·业界资讯
揽星河..9 小时前
【C++】类和对象
c++·算法
D e re k ZH9 小时前
C++ 并发、未定义行为与工程化难点(No 3)
数据结构·c++·算法·面试