Time Limit:
10000/5000MS (Java/Others)
Memory Limit:
128000/64000KB (Java/Others)
Submit Status
Problem Description
背包背包!
唐老师非常开心的在给小彭玉讲背包问题,“01背包就是在M件物品取出若干件放在空间为W的背包里,每件物品的体积为W1,W2……Wn,与之相对应的价值为P1,P2……Pn。求出获得最大价值的方案。”
在讲完这句话,唐老师就给小彭玉出了道01背包的课后习题,但是小彭玉不会做,那么就只能给你做了~
“有N个物品,你的背包空间为W,每个物品的体积和价值都是W1,W2……Wn。求最大能获得多大价值?”
Input
输入第一行有两个整数N,W。 1<=N<=40,0<=W<=100000000。
接下来一行有N个数,表示物品的体积和价值,W[i]<=100000000。
Output
输出一个整数,表示我们能够获得的最大价值。
Sample Input
Sample Output
题目大意:超大背包问题。
分析:折半枚举。先枚举前一半的物品,然后去掉w[i]<=w[j]且v[i]>=v[j]的j,再枚举后一半的物品,接着利用二分搜索,找出前半部分加后半部分的最大价值。
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
struct P {
LL w, v;
P(){}
P(int w, int v):w(w), v(v) {}
bool operator <(const P & cmp) const {
return w < cmp.w;
}
}ps[1<<(50/2)];
int w[50];
int n, W;
int main() {
while(~scanf("%d%d", &n, &W)) {
for(int i = 0; i < n; i++)
scanf("%d", &w[i]);
//枚举前半部分
int n2 = n/2;
for(int i = 0; i < 1<<n2; i++) {
LL sw = 0, sv = 0;
for(int j = 0; j < n2; j++) {
if(i>>j & 1) {
sw += w[j];
sv += w[j];
}
}
ps[i] = P(sw, sv);
}
//去掉多余的元素
sort(ps, ps+(1<<n2));
int m = 1;
for(int i = 1; i < 1<<n2; i++)
if(ps[m-1].v < ps[i].v)
ps[m++] = ps[i];
//枚举后半部分
LL ans = 0;
for(int i = 0; i < 1<<(n-n2); i++) {
LL sw = 0, sv = 0;
for(int j = 0; j < n-n2; j++) {
if(i>>j & 1) {
sw += w[n2+j];
sv += w[n2+j];
}
}
if(sw <= W) {
LL tv = (lower_bound(ps, ps+m, P(W-sw, 1<<30))-1)->v;
ans = max(ans, sv+tv);
}
}
printf("%lld\n", ans);
}
return 0;
}