[KCOJ20170214]又一个背包
题目描述 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\n",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\n",ans+solve(1,n,V)); 52 return 0; 53 }