题目描述 Description
小W要去军训了!由于军训基地是封闭的,小W在军训期间将无法离开军训基地。所以他没有办法出去买他最爱吃的零食。万般无奈的小W只好事先买好他爱吃的零食,装在背包里带入军训基地。市场里的零食琳琅满目,纵然小W想把他们都带走,然而小W的背包只有有限的容量。带哪些零食好呢?小W给每种零食都评估了一个他的喜爱程度。另外,由于市场的零食都是散称售卖的,所以一种零食可以只买一部分带走。现在小W希望能挑选出最合适的购买方案,使得所购买的零食能装进背包且喜爱程度总分最大。因为零食的种类实在是太多了,所以小W找到了聪明的你,希望你能尽快(军训的车队即将出发,时间紧迫)告诉他购买方案。
输入描述 Input Description

第一行两个整数n和w,分别表示有n种零食种类和背包总容量。 接下来n行,每行两个整数ci和vi,分别表示第i种食品占的体积和小W的喜爱程度。

输出描述 Output Description
一行一个数字,表示最优购买方案下,所能获得的最大喜爱程度总和。保留3位小数。

样例输入 Sample Input

5 5
1 5
2 4
3 3
4 2
5 1

样例输出 Sample Output

11.000

数据范围及提示 Data Size & Hint

n <= 2000000,w <= 2*10^9,0<=ci <= 100,0<=vi<=100

一道部分背包问题。这道题按性价比排序然后贪心取肯定是没有问题的,但是复杂度是O(n log n)有点大,像我们学校OJ对于n=2000000的肯定是跑不下来的。所以我们要O(n)做这道题。乍一想,发现完全没有思路,应该有一点灵感就是类似于O(n)求第K大数一样,一个用的是O(n log n)排序,一个是nth_element O(n)做的。于是我们就想分治做这道题,把问题分为更小的子问题。算出每个物品的性价比之后,我们nth_element求出中位数,这样这个数列的左边就变成比中位数小的,右边就变成比中位数大的。然后我们暴力扫一遍右边的价值和,判断一下,然后就好办了。复杂度分析的话,这个东西1+1/2+1/4+1/8+…+1/2^n是小于2的,所以类似的话,复杂度就是O(n)。本题还有一个坑点,就是空间有可能是0!

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cstdio>
 4 #include<cstdlib>
 5 #include<cstring>
 6 #include<queue>
 7 using namespace std;
 8 typedef long long LL;
 9 inline int read()
10 {
11     int x=0,f=1;char c=getchar();
12     while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
13     while(isdigit(c)){x=x*10+c-'0';c=getchar();}
14     return x*f;
15 }
16 const int maxn=2000010;
17 struct Item
18 {
19     double w,v,s;
20     bool operator < (const Item &t)const  {return s<t.s;}
21 }a[maxn];
22 int n,ans;
23 double V;
24 double solve(int l,int r,double val)//val是当前背包容量 
25 {
26     if(l>r)return 0;
27     if(l==r)
28     {
29         if(val<a[l].v)return val*a[l].s;
30         else return a[l].w;
31     }
32     int mid=(l+r)/2;
33     double sum=0,sum2=0;
34     nth_element(a+l,a+mid,a+r+1);
35     for(int i=mid+1;i<=r;i++)sum+=a[i].w,sum2+=a[i].v;
36     //printf("%d %d %d %lf %lf %lf
",l,r,mid,sum,sum2,val);
37     if(val>sum2)return sum+solve(l,mid,val-sum2);
38     if(val==sum2)return sum;
39     if(val<sum2)return solve(mid+1,r,val);
40 }
41 int main()
42 {
43     n=read();V=(double)read();
44     int i=1;
45     while(i<=n)
46     {
47          a[i].v=(double)read();a[i].w=(double)read();
48          if(a[i].v==0)ans+=a[i].w,n--;
49          else a[i].s=a[i].w/a[i].v,i++;
50     }
51     printf("%.3f
",ans+solve(1,n,V));
52     return 0;
53 }

View Code