BZOJ 1190: [HNOI2007]梦幻岛宝珠

时间:2022-01-24 18:57:25

关于题意不多说了,根据a*2^b很明显发现可以根据b来进行DP

然后 分b层 f[i][j]表示 j*2^i下获得的最大价值 关于后面的位先不理 我们便可以进行01背包

接着 我们可以发现 如果w的第i位为0  j为1的时候可能装不下,这时如果你想保存这个状态 就需要在前一位-1,大概就是这个意思,自己好好想一想吧,毕竟这里没有口述和白板。

膜拜PoPoQQQ , 参考抄袭了一下他的简洁代码

其实不建议像我这样打,时间复杂度比较大。。我的代码好像跑的很慢,,,知道思路就好了

#include<cstdio>
#include<cstring>
#include<algorithm>
#define me(a,x) memset(a,x,sizeof a)
using namespace std;
int ans,f[35][1010];
int main()
{
int n,w,i,j,x,k;
while(scanf("%d%d",&n,&w))
{
if(n<0)break; ans=0; me(f,0);
for(i=1;i<=n;i++)
{
int a,b=0;
scanf("%d%d",&a,&x);
while(~a&1)
a>>=1,b++;
for(j=1000;j>=a;j--)
f[b][j]=max(f[b][j],f[b][j-a]+x);
}
for(i=0;i<=30;i++)
for(j=1;j<=1000;j++)
f[i][j]=max(f[i][j],f[i][j-1]);
for(i=1;i<=min(w,1000);i++) ans=max(ans,f[0][i]);
for(i=1;i<=30&&(1<<i)<=w;i++)
for(j=min(1000,w>>i);j>=0;j--)
{
int u=(w>>i-1)&1;
for(k=0;k<=j;k++)
f[i][j]=max(f[i][j],f[i][j-k]+f[i-1][min((k<<1)+u,1000)]);
ans=max(ans,f[i][j]);
}
printf("%d\n",ans);
}
return 0;
}