超大背包(二分+贪心)

Description

有重量和价值分别为 wi ( 1 ≤ wi ≤ 1015 )、vi ( 1 ≤ vi ≤ 1015 ) 的 n (1 ≤ n ≤ 40 )个物品。从这些物品中挑选总重量不超过 C (1 ≤ C ≤ 1015)的物品,求所选挑选方案中价值总和的最大值。

Input

多测试用例。每个测试用例:

第一行是 nC,接下来有 n 行,每行两个正整数,分别是各个物品的 wivi

Output

每个测试用例输出一行:最大价值。

Sample Input

4 5
2 3
1 2
3 4
2 2

Sample Output

7

#include<iostream>
#include<algorithm>
#include<cstdio>
#include <vector>
#define LL long long 
//#define  maxx 0x3f3f3f3f 
const LL inf=0x3f3f3f3f;
using namespace std;
pair<LL,LL> dp[1<<21];//重量,价值
int n,n2;
LL C,sumw,sumv,ew,w[42],v[42];

int main(){
    while(~scanf("%d %lld",&n,&C)){
        for(int i=0;i<n;i++){
            scanf("%lld %lld",&w[i],&v[i]);
        }
         n2=n/2;
        for(int i=0;i<(1<<n2);i++){//枚举前半部分,i的二进制的每一位算一种情况
             sumw=0;sumv=0;
            for(int j=0;j<n;j++){
                if(i>>j&1)//i<<n2种选择情况,判断此种情况是否需要加上去 
                {
                    sumw+=w[j];
                    sumv+=v[j];
                }
            }
            dp[i]=make_pair(sumw,sumv);
        }
    
        sort(dp,dp+(1<<n2));//把dp的重量从小到大排序
        //贪心的做法,优化当前情况,排序后若价值与重量没法一致,则抛弃 
    int    now=1;
        for(int i=1;i<(1<<n2);i++){
            if(dp[now-1].second<dp[i].second){
                dp[now++]=dp[i];
            }
        } 
    
    LL sum=0;
    for(int i=0;i<(1<<(n-n2));i++){
        sumw=0;//printf("1\n");
        sumv=0;
        for(int j=0;j<(n-n2);j++){
        if(i>>j&1){
            sumw+=w[n2+j];
            sumv+=v[n2+j];
        }}
        if(sumw<=C)
        {
        //二分的做法找出第一个比maxx同样重量或更大时候,最大价值然而肯定没有,则减一便是最大 
        LL    ew = (lower_bound(dp, dp + now, make_pair(C - sumw, inf)) - 1) -> second;
            sum=max(sum,sumv+ew);
            
        }    
    }
    printf("%lld\n",sum);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Yum20/p/9577528.html
今日推荐