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存住值
#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;
}