题目:
有n个物品的重量和价值分别为wi,vi,从中选取k个物品,使得单位重量的价值最大
样例:
输入:
n=3
k=2
(w,v)={ (2 , 2) , (5 , 3) , (2 , 1) }
输出
0.75(选0号和2号 ( 2 + 1)/( 2 + 2) = 0.75)
思路
首先想到的方法是先把物品按照单价排序,再从大到小进行选取。但是这样选出来的不一定是最优的,例如上面的案例,如果按照贪心算法 应该是选择 0号和1号, 结果为(2+3)/(2+5)=0.714。所以这个方法是不行的。
对于这个问题,使用二分搜索法可能很好的解决。我们定义:
条件C(x):=可以选择使得单位重量的价值不小于x
因此,原问题就变成了求满足C(x)的最大的x。
怎么判断C(x)是否可行呢?即判断是否
∑i∈kvi/∑i∈kwi≥x
把这个不等式变形就得到
∑i∈k(vi?x×wi)≥0
因此,原来的C(x):=vi?x×wi 从大到小排列,然后选出前k个的和大于等于0
代码:
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
bool C(double x,int n,int k,int* w,int* v)
{
//用于保存所有vi-x*wi
double y[n];
for(int i=0;i<n;i++){
y[i]=v[i]-x*w[i];
}
sort(y,y+n);
//计算后k个到和是否大于0 (因为是按照升序排序的)
double sum = 0;
for(int i=0;i<k;i++){
sum+=y[n-i-1];
}
return sum>=0;
}
int main()
{
int n,k;
int max_value=0;
cout<<"n = ";
cin>>n;
cout<<"k = ";
cin>>k;
int *w=new int[n];
int *v=new int[n];
for(int i=0;i<n;i++){
cin>>w[i];
}
for(int i=0;i<n;i++){
cin>>v[i];
if(max_value<v[i])
max_value=v[i];
}
double lb=0,ub=max_value;
for(int i=0;i < 100;i++){
double mid=(lb + ub )/2;
if(C(mid,n,k,w,v))
lb=mid;
else
ub=mid;
}
delete[] w,v;
printf("%.2f\n",ub);
}
运行结果:
n = 3
k = 2
2 5 2
2 3 1
0.75
时间: 2024-11-10 12:03:54