题目描述:
解题思路:
假设小咪只想买3件物品,并且最终买了物品 i , j , k i,j,k i,j,k使得所求的最大值为 m a x max max。则有关系式 v [ i ] + v [ j ] + v [ k ] c [ i ] + c [ j ] + c [ k ] = m a x \frac{v[i]+v[j]+v[k]}{c[i]+c[j]+c[k]}=max c[i]+c[j]+c[k]v[i]+v[j]+v[k]=max成立,也即 v [ i ] + v [ j ] + v [ k ] − m a x ∗ c [ i ] − m a x ∗ c [ j ] − m a x ∗ c [ k ] = 0 v[i]+v[j]+v[k]-max*c[i]-max*c[j]-max*c[k]=0 v[i]+v[j]+v[k]−max∗c[i]−max∗c[j]−max∗c[k]=0成立。假如我们随便猜一个数 x x x去替代" v [ i ] + v [ j ] + v [ k ] − m a x ∗ c [ i ] − m a x ∗ c [ j ] − m a x ∗ c [ k ] v[i]+v[j]+v[k]-max*c[i]-max*c[j]-max*c[k] v[i]+v[j]+v[k]−max∗c[i]−max∗c[j]−max∗c[k]"中的 m a x max max,如果x被我们猜大了则 v [ i ] + v [ j ] + v [ k ] − x ∗ c [ i ] − x ∗ c [ j ] − x ∗ c [ k ] < 0 v[i]+v[j]+v[k]-x*c[i]-x*c[j]-x*c[k]<0 v[i]+v[j]+v[k]−x∗c[i]−x∗c[j]−x∗c[k]<0,若 x x x被猜小了则 v [ i ] + v [ j ] + v [ k ] − x ∗ c [ i ] − x ∗ c [ j ] − x ∗ c [ k ] > 0 v[i]+v[j]+v[k]-x*c[i]-x*c[j]-x*c[k]>0 v[i]+v[j]+v[k]−x∗c[i]−x∗c[j]−x∗c[k]>0。由此,我想到可以将此题转化为二分搜索:在可能的答案区间里猜这个最大值,猜大了就往小了猜,猜小了就往大了猜。直到这个数接近 m a x max max即可。
AC代码:
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1e4+100;
int n,k;
double c[maxn],v[maxn];
bool check(double x){
double y[maxn];
for(int i=1;i<=n;i++){
y[i]=v[i]-x*c[i];
}
sort(y+1,y+n+1);
double s=0;
for(int i=n;i>n-k;i--){ //选择前k个最大y[i]累加,如果最大的累加和都小于0则这个数一定猜大了
s+=y[i];
}
return s>=0; //s>=0则表示猜小了。也即返回true时表示猜小了
}
int Bsearch(double l,double r){
int t=100; //猜100次就足以让答案满足“精确至整数”的精度。
while(t--){
double m=(l+r)/2;
if(check(m)) l=m; //猜小了就往大了猜
else r=m; //猜大了就往小了猜
}
return (int)l;
}
int main(){
int t;
cin>>t;
while(t--){
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>c[i]>>v[i];
}
double l=0;
double r=1e4; //大致估计答案在0-10000之间
cout<<Bsearch(l,r)<<endl;
}
return 0;
}