题意 : 给出 N 个物品的价值和重量,然后要求选出 K 个物品使得选出来物品的单位重量价值最大,最后输出被选物品的编号。
分析 :
很容易去想先算出每个物品的单位价值然后升序排序取前 K 个,但是很可惜这样的做法是错误的。
例如 : N = 3、K = 2、{ w、v } = { {2,2}、{5,4}、{2,1} },贪心的方法是选出 1、2,但是正确答案是选出1、3
这题的正确做法是利用二分,难点就在如何判定二分出来的每一个单位重量价值是否是一个可行答案
假设当前二分出来的答案是 x
那么就要求选出来的前 K 个物品 ∑ vi / ∑ wi ≥ x
变形得到 ∑ ( vi - wi * x ) ≥ 0
那么最后就变成了对于当前的 x 对所有物品的 vi - wi * x 进行排序
并判断前 K 大的是否大于等于 0 ,如果是则说明可行!
#include<algorithm> #include<stdio.h> #include<math.h> using namespace std; const int maxn = 1e5 + 10; const double EPS = 1e-8; const double INF = 10000000; struct Jewel{ int id, v, w; }arr[maxn]; struct st{ double val; int id; bool operator < (const st & rhs) const{ return this->val > rhs.val; }; }c[maxn]; int N, K, ans[maxn]; bool OK(double key) { for(int i=1; i<=N; i++){ c[i].val = arr[i].v - arr[i].w * key; c[i].id = arr[i].id; } sort(c+1, c+1+N); double sum = 0; for(int i=1; i<=K; i++) sum += c[i].val; if(sum >= 0.0){ for(int i=1; i<=K; i++) ans[i] = c[i].id; return true; }else return false; } int main(void) { while(~scanf("%d %d", &N, &K)){ for(int i=1; i<=N; i++){ if(i <= K) ans[i] = i;///注意要提前给 ans 赋上初值,因为 OK 函数可能永远不为真,例如所有物品的 v = 0 arr[i].id = i; scanf("%d %d", &arr[i].v, &arr[i].w); } double L = 0.0; double R = INF; while(R - L > EPS){ double mid = ( R + L ) / 2.0; if(OK(mid)) L = mid; else R = mid; } for(int i=1; i<=K; i++){ printf("%d", ans[i]); if(i < K) putchar(‘ ‘); else putchar(‘\n‘); } } return 0; }