Yougth现在有n个物品的重量和价值分别是Wi和Vi,你能帮他从中选出k个物品使得单位重量的价值最大吗?
3 2 2 2 5 3 2 1
0.75
分析:
要想取得单位重量价值最大,需要在0-Max之间二分搜索,Max为任意选物品所得的单位重量最大价值,
Max = max(v[i] / w[i] | i = 0...n - 1)。
为什么Max如此取值,下面证明:
假设一对vi,wi 满足: vi / wi = max(v[i] / w[i] | i = 0......n-1),任取一对 vk,wk (k != i),则有vi / wi > vk / wk,
即:vi * wk > vk * wi 把它记为公式1。
证明:
要证的是 (vi + vk1 +...+ vk2) / (wi + wk1 +...+ wk2) <= vi / wi, (0<=k1<=k2<=n-1 && k1 != i && k2 != i),
1.当k1 = k2 = 0 时,即不增加,上式等号成立。
2.为便于证明,此处只增加vk(k != i),它具有普通性。即证(vi + vk) / (wi + wk) < vi / wi,
即wi(vi + vk) < vi(wi + wk),也就是vi * wk > vk * wi,此式即为公式1,成立!
3.当增加2......n-1 个数时也成立。
4.证毕。
对于cleck(a)若不理解见代码下面详解。
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> using namespace std; const int maxn = 10000 + 5; double x[maxn], w[maxn], v[maxn]; //全定义为double,便于计算 int n, k; bool cleck(double a){ for(int i = 0; i < n; i++) x[i] = v[i] - a * w[i]; sort(x, x + n); double sum = 0; for(int i = 0; i < k; i++) sum += x[n - i - 1]; //贪心,由大到小取 return sum >= 0 ? true : false; } int main() { while(~scanf("%d%d", &n, &k)){ double Max = 0; for(int i = 0; i < n; i++){ scanf("%lf%lf", &w[i], &v[i]); Max = max(Max, v[i] / w[i]); } double L = 0, R = Max; while(R - L > 1e-4){ //二分查找最优值 double M = (L + R) / 2; if(cleck(M)) L = M; else R = M; } printf("%.2lf\n", L); } return 0; }
(vk1 +...+ vk2) / (wk1 +...+ wk2) >= a (0<=k1<=k2<=n-1),
即vk1 +...+ vk2 >= a(wk1 +...+ wk2),
即vk1 - a * wk1 +...+ vk2 - a * wk2 >= 0
所以尽量取vki - a * wki(即代码中xi)大的才有可能满足。
NYOJ 914 Yougth的最大化,布布扣,bubuko.com
原文:http://blog.csdn.net/houheshuai/article/details/38442641