版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Maxwei_wzj/article/details/83274676
测试地址:梦幻岛宝珠
做法: 本题需要用到背包DP+思维。
这道题看上去是一个裸01背包,然而容量特别大,因此我们只能从其中唯一一个特殊条件入手:
形式的重量。
我们考虑把这些物品分阶段来进行决策。我们首先对每个
,求出重量表示为
的那些物品中,取重量为
的物品能得到的最大总价值
,这就是一个一般的01背包了,因为
,所以时间复杂度最多是
的级别。接下来,我们从低位向高位DP,令
为取用
的物品,容量为
加上
在第
位以下的所有容量时所能得到的最大总价值。上面
在第
位以下的容量,就指的是它在第
位以下的上界(用位运算解释就是
)。在转移的时候,我们首先枚举当前的
,然后看前一位有哪些可以转移到当前状态的状态。显然,由于要满足第
位之前是上界的条件,第
位应该和
的第
位相同,这才能转移。于是我们就写出来了一个状态转移的式子,就解决了这道题,时间复杂度虽然看上去有点大,但均摊下来还是跑得很快的。
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
int n,W,totw[35],f[35][2100],len;
vector<int> w[35],val[35];
int main()
{
while(scanf("%d%d",&n,&W)&&n>=0&&W>=0)
{
len=0;
for(int i=0;i<35;i++)
w[i].clear(),val[i].clear();
memset(totw,0,sizeof(totw));
memset(f,0,sizeof(f));
for(int i=1;i<=n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
int j=0;
while(!(x&1)) {j++;x>>=1;}
w[j].push_back(x);
totw[j]+=x;
val[j].push_back(y);
len=max(len,j);
}
while(W>>len) len++;
len--;
for(int i=0;i<=len;i++)
for(int j=0;j<(int)w[i].size();j++)
for(int k=totw[i];k>=w[i][j];k--)
f[i][k]=max(f[i][k],f[i][k-w[i][j]]+val[i][j]);
for(int i=1;i<=len;i++)
{
totw[i]+=((totw[i-1]+1)>>1);
for(int j=totw[i];j>=0;j--)
for(int k=0;k<=j;k++)
f[i][j]=max(f[i][j],f[i][j-k]+f[i-1][min(totw[i-1],(k<<1)|((W>>(i-1))&1))]);
}
printf("%d\n",f[len][1]);
}
return 0;
}