超大背包问题

使用dp求解复杂度过高,利用n比较小的特点。

折半枚举,把前半部分中的选取方法对应的重量和价值总和记为w1,v1,这样在后半部分寻找总重w2<=W-w1时使v2最大的选取方法就好。

首先排除所有w2[i]<=w2[j]&&v2[i]>=v1[j]的j。这一点可以按照w2,v2的字典序排序后做到。

此后剩余的元素都满足w2[i]<w2[j]&&v2[i]<v2[j],要计算max{v2|w2<=W'},只要寻找满足w2[i]<=W'的最大i即可,用二分搜索完成。

#include<iostream>
#include<math.h>
#include<algorithm>
#include<stdio.h>
#define INF 10000000
typedef long long ll;
using namespace std;
int n;
ll w[41],v[41],W;
pair<ll,ll> ps[1<<21];
int main()
{
    cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>w[i]>>v[i];
    }
    cin>>W;
    //枚举前半部分 二进制枚举
    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+=v[j];
            }
        }
        ps[i]=make_pair(sw,sv);
    }

    //去掉多余元素(价值小重量大)
    sort(ps,ps+(1<<n2));
    int m=1;
    for(int i=1;i<1<<n2;i++)
    {
        if(ps[m-1].second<ps[i].second)
        {
            ps[m++]=ps[i];
        }
    }

    //枚举后半部分并求解
    ll res=0;
    for(int i=0;i<1<<(n-n2);i++)
    {
        ll sw=0,sv=0;
        for(int j=0;j<n-n2;j++)
        {
            if(j>>i&1)
            {
                sw+=w[n2+j];
                sv+=v[n2+j];
            }
        }
        if(sw<=W)
        {
            //找到前半部分中加起来不超重的价值最大者
            ll tv=(lower_bound(ps,ps+m,make_pair(W-sw,(ll)INF))-1)->second;
            res=max(res,sv+tv);
        }
    }
    cout<<res<<endl;
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/wangkaipeng/p/6502947.html