K Best
Description Demy has n jewels. Each of her jewels has some value vi and weight wi. Since her husband John got broke after recent financial crises, Demy has decided to sell some jewels. She has decided that she would keep k best jewels for herself. She decided to keep such jewels that their specific value is as large as possible. That is, denote the specific value of some set of jewels S = {i1, i2, …, ik} as
Demy would like to select such k jewels that their specific value is maximal possible. Help her to do so. Input The first line of the input file contains n — the number of jewels Demy got, and k — the number of jewels she would like to keep (1 ≤ k ≤ n ≤ 100 000). The following n lines contain two integer numbers each — vi and wi (0 ≤ vi ≤ 106, 1 ≤ wi ≤ 106, both the sum of all vi and the sum of all wi do not exceed 107). Output Output k numbers — the numbers of jewels Demy must keep. If there are several solutions, output any one. Sample Input 3 2 1 1 1 2 1 3 Sample Output 1 2 Source
Northeastern Europe 2005, Northern Subregion
|
题意:有n个物品的重量和价值分别是wi和vi。从中选出k个物品使得单位重量的价值最大。输出这k个物品的编号。
分析:在书中看到此题,觉得明显是每个都求出它的单位价值,然后排序找前k个就行。可是{vi,wi} = {{2,2}, (3,5}, {1,2}}这组数据就无法通过,我那种方法是选1号和2号,但答案是选1号和3号。所以那种方法是不行的,也是证明不了的。这题可以用二分来做,设x为k个物品的单位价值,只要找出满足条件x的最大值即可,Sigma{vi}/Sigma{wi} >= x,转化为 Sigma{vi-x*wi} >= 0 因此可以对(vi-x*wi)>=0的值进行排序贪心地进行选取(保证前k个最大,这样直接求和即可知此时的x值是否满足表达式:前k个的和不小于0)。对于用二分寻找x的方法,需要找出所有值或寻求一个表达式(表达式可能需要进行转化),并且表达式的未知数只有一个,且只有单调函数才能进行二分。
code:
#include<stdio.h>
#include<math.h>
#include<algorithm>
using namespace std;
int n, k;
struct P
{
int v, w, num;
double y;
}p[100010];
bool cmp(P c, P d) {return c.y > d.y;}
bool C(double x) //判断是否满足条件
{
for(int i=0; i<n; i++)
p[i].y = p[i].v-x*p[i].w;
sort(p,p+n,cmp); //通过结构体排序保存序号
double sum = 0;
for(int i=0; i<k; i++)
sum += p[i].y; //求前k个数的和
return sum >= 0;
}
int main()
{
scanf("%d%d", &n,&k);
for(int i=0; i<n; i++)
{
scanf("%d%d", &p[i].v,&p[i].w);
p[i].num = i+1;
}
double lb = 0, ub = 1e6+10;
while(ub-lb >= 1e-8)
{
double mid = (lb+ub)/2;
if(C(mid)) lb = mid;
else ub = mid;
} //由于最后肯定能求出解,所以数组中的序号也会随之更新,
printf("%d", p[0].num);
for(int i=1; i<k; i++)
printf(" %d", p[i].num);
puts("");
return 0;
}