超大背包问题:有n个重量和价值分别为w[i]和v[i]的物品,从这些物品中挑选总重量不超过W的物品,求所有挑选方案中价值总和的最大值。其中,1 ≤ n ≤ 40, 1 ≤ w[i], v[i] ≤ 10^15, 1 ≤ W ≤ 10^15.
按照普通的DP 思路显然是无法求解的, 背包的体积太大了, 那么就要换种思考的方向,观察物品的数量只有 40 个,普通的枚举话 2 ^ 40,肯定是超时,那么如何是分成两堆呢 ? 在按照字典序去枚举,不就没问题了吗 ?
int n, W;
int v[50], w[50];
struct node
{
int ww, vv;
node(int _w = 0, int _v = 0):ww(_w), vv(_v){}
}pre[1<<25];
bool cmp(node a, node b){
if (a.vv == b.vv) return a.ww > b.ww;
else return a.vv < b.vv;
}
int ans;
int fun(int l, int r, int key){
while(l <= r){
int mid = (l + r) >> 1;
if (pre[mid].vv == key) return pre[mid].ww;
else if (pre[mid].vv < key) l = mid + 1;
else r = mid - 1;
}
return pre[r].ww;
}
void solve(){
int n2 = n / 2;
for(int i = 0; i < 1 << n2; i++){
int sv = 0, sw = 0;
for(int j = 0; j < n2; j++){
if ((i >> j) & 1){
sw += w[j];
sv += v[j];
}
}
pre[i] = node(sw, sv);
}
sort(pre, pre+ (1 << n2), cmp);
int m = 0;
for(int i = 1; i < 1<<n2; i++){
if (pre[m].ww < pre[i].ww){
pre[++m] = pre[i];
}
}
ans = 0;
for(int i = 0; i < 1<<(n-n2); i++){
int sv = 0, sw = 0;
for(int j = 0; j < (n-n2); j++){
if ((i >> j) & 1){
sw += w[n2+j];
sv += v[n2+j];
}
}
if (sv <= W){
int f = fun(0, m, W-sv);
ans = max(ans, f + sw);
}
}
}
int main() {
//freopen("in.txt", "r", stdin);
//freopen("out.txt", "w", sttout);
while(~scanf("%d%d", &n, &W)){
for(int i = 0; i < n; i++){
scanf("%d%d", &v[i], &w[i]);
}
solve();
printf("%d
", ans);
}
return 0;
}
/*
4 5
2 3
1 2
3 4
2 2
*/