[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;	
}
相关推荐
2301_790300961 天前
C++与微服务架构
开发语言·c++·算法
重生之我是Java开发战士1 天前
【优选算法】前缀和:一二维前缀和,寻找数组的中心下标,除自身以外数组的乘积,和为K的子数组,和可被K整除的子数组,连续数组,矩阵区域和
线性代数·算法·矩阵
梵刹古音1 天前
【C语言】 循环结构
c语言·开发语言·算法
bbq粉刷匠1 天前
Java-排序2
java·数据结构·排序算法
皮皮哎哟1 天前
冒泡排序与数组传递全解析 一维二维指针数组及二级指针应用指南
c语言·算法·冒泡排序·二维数组·指针数组·传参·二级指针
m0_561359671 天前
C++代码冗余消除
开发语言·c++·算法
近津薪荼1 天前
优选算法——滑动窗口1(单调性)
c++·学习·算法
diediedei1 天前
嵌入式C++驱动开发
开发语言·c++·算法
燃于AC之乐1 天前
《算法实战笔记》第10期:六大算法实战——枚举、贪心、并查集、Kruskal、双指针、区间DP
算法·贪心算法·图论·双指针·区间dp·二进制枚举
diediedei1 天前
高性能计算通信库
开发语言·c++·算法