哗啦啦族的01背包问题(折半枚举)

E - 哗啦啦族的01背包问题

Time Limit:  10000/5000MS (Java/Others)     Memory Limit:  128000/64000KB (Java/Others)
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
4 10
4 3 5 11
Sample Output
9
题目大意:超大背包问题。


分析:折半枚举。先枚举前一半的物品,然后去掉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;
}










评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值