BZOJ 1190: [HNOI2007]梦幻岛宝珠

关于题意不多说了,根据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;
}


你可能感兴趣的:(BZOJ 1190: [HNOI2007]梦幻岛宝珠)