[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;	
}
相关推荐
泡沫冰@4 小时前
数据结构(11)
数据结构
奶茶树4 小时前
【数据结构】二叉搜索树
数据结构·算法
小苏兮4 小时前
【数据结构】二叉搜索树
开发语言·数据结构·c++·学习·1024程序员节
晨曦(zxr_0102)4 小时前
CSP-X 2024 复赛编程题全解(B4104+B4105+B4106+B4107)
数据结构·c++·算法
ai安歌4 小时前
【Rust编程:从新手到大师】 Rust 控制流深度详解
开发语言·算法·rust
Shinom1ya_4 小时前
算法 day 36
算法
·白小白4 小时前
力扣(LeetCode) ——15.三数之和(C++)
c++·算法·leetcode
海琴烟Sunshine4 小时前
leetcode 268. 丢失的数字 python
python·算法·leetcode
CL.LIANG4 小时前
视觉SLAM前置知识:相机模型
数码相机·算法