最大化平均值

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)

一般最先想到的方法可能是把物品按照单位价值排序,从大到小贪心的选取。但是这个方法对于样例得到结果是5/7 = 0.714。

实际上,对于这个问题使用二分搜索法可以很好的解决。我们定义

条件C(x):=可以选择使单位重量的价值不小于x

因此原问题就变成求满足C(x)的最大x。假设我们选了某个物品的集合S,那么它们的单位重量价值为

vi / ∑wi

因此变成判断是否存在S满足下面的条件

vi / ∑wi >= x,即 ∑(vi - x * wi) >= 0

因此,可以对(vi - x * wi)进行排序贪心地选取

C(x) = ((vi - x * wi)从大到小排列中的前k个的和不小于0)

每次判断的复杂度为O(nlogn)。

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstdio>
 4 using namespace std;
 5 
 6 #define INF        1000
 7 #define MAX_N    1000
 8 
 9 int n, k;
10 int w[MAX_N], v[MAX_N];
11 double y[MAX_N];
12 
13 //判断是否满足条件
14 bool C(double x)
15 {
16     for (int i = 0; i < n; i++)
17     {
18         y[i] = v[i] - x * w[i];
19     }
20     sort(y, y+n);
21 
22     //计算y数组中从大到小前k个数的和
23     double sum = 0;
24     for (int i = 0; i < k; i++)
25     {
26         sum += y[n-i-1];
27     }
28     return sum >= 0;
29 }
30 
31 void solve()
32 {
33     double lb = 0, ub = INF;
34     for (int i = 0; i < 100; i++)
35     {
36         double mid = (lb + ub) / 2;
37         if (C(mid)) lb = mid;
38         else ub = mid;
39     }
40     printf("%.2f\n", ub);
41 }
42 
43 
44 int main()
45 {
46     cin >> n >> k;
47     for (int i = 0; i < n; i++)
48     {
49         cin >> w[i] >> v[i];
50     }
51     solve();
52     return 0;
53 }

 

posted @ 2014-10-29 10:14  bournet  阅读(233)  评论(0编辑  收藏  举报