题解:P13013 [GESP202506 五级] 奖品兑换

最后更新于 2025-08-03 09:55:36
分类 题解

题意分析:

1.使用 a 张课堂优秀券和 b 张作业优秀券兑换一份奖品。

2.使用 b 张课堂优秀券和 a 张作业优秀券兑换一份奖品。

现在小 A 有 n 张课堂优秀券和 m 张作业优秀券,求他最多能兑换多少份奖品。

思路:

显然,由于题目对时间复杂度有着较高的要求,暴力枚举根本行不通,于是我们想到了二分答案。

设使用兑换 x 张 1 类奖品, y 张 2 类奖品。 显然,共兑换 x+y 个奖品。

则有:

$ax+by≤n,ay+bx≤m$

不等式两边分别相加,得

$ax+ay+bx+by≤m+n$

化简,得

$x+y≤(n+m)/(a+b)$

易知,(x+y)一定在该区间中。 可以二分答案(x+y)。

当我们二分后,要进行判断这种组合是否符合要求。

于是……继续二分答案!!!

我们可以二分x,由此计算y,再进行判断

$ax+by≤n,ay+bx≤m$

得出该种组合是否存在。若存在,则尽量让(x+y)最大。

同时,对于每一种合法的(x+y),都要用ans存住值

AC code:

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,a,b;
signed main(){
	cin>>n>>m>>a>>b;
	if(n>m)swap(n,m);
	if(a>b)swap(a,b);
	int ans=0;
	int all=(n+m)/(a+b);     //ans上限
	int left=0,right=all;
	int mid;
	while(left<=right){      //二分答案x+y
		mid=(left+right)/2;
		int l=0,r=mid;
		bool flag=false;
		while(l<=r){           //二分答案判断是否合法
			int mid2=(l+r)/2;
			int sum1=a*mid2+b*(mid-mid2);
			int sum2=b*mid2+a*(mid-mid2);
			if(sum1>n&&sum2>m)break;
			if(sum1>n){
				l=mid2+1;
			}else if(sum2>m){
				r=mid2-1;
			}else{
				flag=true;         //合法,退出循环
				break;
			}
		}if(flag==true){//合法,尽可能多换奖品
			ans=mid;      //一定要存住值
			left=mid+1;
		}else{         //不合法,尝试少换一点
			right=mid-1;
		}
	}cout<<ans<<endl;
	return 0;
}

完结撒花