题意:有n个物品的重量和价值分别是wi和vi。从中选出k个物品使得单位重量的价值最大。
题解:首先考虑二分做法
那么一般最先想到的方法是把物品按照单位价值进行排序,从小到大贪心地进行选取。但是这个方法对于很多数据都有bug,所以是不行的。
实际上,对于这个问题使用二分搜索可以很好地解决,定义:
条件C(x):=可以选择使得单位重量的价值不小于x
因此,原来的问题就变成了求满足C(x)的最大的x。那么怎么判断C(x)是否可行呢?假设我们选了某个物品的集合S,那么它们的单位重量的价值是
因此就变成了判断是否存在S满足下面的条件
把这个不等式进行变形得到;
因此,可以对(vi-x*wi)的值进行排序贪心地进行选取。因此就变成了
C(x)=((vi-x*wi)从大到小排列中地前K个的和不小于0)
每次判断的复杂度是O(nlogn).
附上代码:(注意精度问题,以及二分玄学操作)
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=1e5+50;
const int INF=1e7;
int n,k;
int v[maxn],w[maxn];
struct node{
double val;
int id;
};
node nodes[maxn];
bool cmp(node a,node b)
{
return a.val>b.val;
}
int ok(double x)
{
for(int i=0;i<n;i++){
nodes[i].val=v[i]-x*w[i];
nodes[i].id=i+1;
}
sort(nodes,nodes+n,cmp);
double sum=0;
for(int i=0;i<k;i++){
sum+=nodes[i].val;
}
return sum>=0;
}
int main()
{
scanf("%d%d",&n,&k);
for(int i=0;i<n;i++){
scanf("%d%d",&v[i],&w[i]);
}
double lb=-1,ub=INF;
while(ub-lb>1e-8){
double mid=(lb+ub)/2;
if(ok(mid)){
lb=mid;
}else{
ub=mid;
}
}
for(int i=0;i<k;i++){
printf("%d",nodes[i].id);
if(i<k-1){
printf(" ");
}else{
printf("\n");
}
}
return 0;
}