S-小咪买东西_2021秋季算法入门班第三章习题:二分、三分、01 (nowcoder.com)
数据范围:
1≤T≤10
1≤n≤104
1≤k≤n
1≤ci,vi≤104
知识点:01分数规划+二分
分析:1.已知要求总价值/总花费(即单位价值) ,设单位价值为x,则x = Σv / Σc。
2.化简得Σv - Σc * x = 0,接下来就是二分答案x。
3.如果我们选取的v和c使这个式子>0的话,说明至少还有一组v和c可以使得x更大:Σv - Σc * x > 0。这就是:x < Σv / Σc(算出了答案可以比二分猜测的x大)
4.所以我们就可以依照这个式子得到每个物品的权值,然后进行排序,选出最大的k个。进行Σv - Σc * x > 0的判断。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e4+10;
struct node{
int c,v,tmp;
}a[N];
bool cmp(node a,node b){
return a.tmp>b.tmp;
}
int n,k;
ll pan(ll x){
ll sum=0,c=0,v=0;
for(int i=0;i<n;i++){
a[i].tmp=a[i].v-a[i].c*x;
}
sort(a,a+n,cmp);
for(int i=0;i<k;i++){
sum+=a[i].tmp;
v+=a[i].v;
c+=a[i].c;
}
if(sum<0)return -1;
else return v/c;//可以大于等于二分答案
}
int main(){
int t;
cin>>t;
while(t--){
ll ans=0;
cin>>n>>k;
for(int i=0;i<n;i++){
cin>>a[i].c>>a[i].v;
}
int l=0,r=1e8+10;//由题目所给范围知,最大范围(n)1e4*(v)1e4
while(l<r){
int mid=(l+r)/2;
int res=pan(mid);
if(res==-1)r=mid-1;
else{
ans=res;
l=mid+1;
}
}
cout<<ans<<endl;
}
return 0;
}