洛谷3188

显然是DP
\(a\times2^{b}\)\(b\)相同的分成一类
\(f[i][j]\)表示\(2\)的指数为\(i\)时,前面的系数为\(j\)(即总的重量为\(j\times2^{i}\))时的最大价值
设当前DP到的这个物品的系数为\(k\),有\(f[i][j]=max(f[i][j],f[i][j-k]+val)\)
考虑合并
\(g[i][j]\)表示当前DP到的种类(即\(2\)的次幂)为\(i\),背包总容量为\(j\times2^{i}+W\)&\(((1<<i)-1)\)(即\(W\)的后\(i\)位)时的最大价值
此时对于当前DP到的这个种类系数一定不能取超过\(j+1\),因为显然有\((j+1)\times2^{i}\)\(j\times2^{i}+W\)&\(((1<<i)-1)\)
那么有\(g[i][j]=max(g[i][j],f[i][k]+g[i-1][2j-2k+((W>>i-1)\)&\(1)])(0≤k≤j)\)
\(j\times2^{i}+W\)&\(((1<<i)-1)-k\times2^{i}\)变形即可得到以上式子
最终的答案为\(g[31][0]\)

code(由于用了vector,常数过大)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+10,M=3e5+10;
int n;
ll W;
ll f[32][1010],g[32][1010];
struct Node
{
	int num;
	ll val;
}temp;
vector<Node> obj[32];
int read()
{
    int x=0,f=1;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-f;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-48;s=getchar();}
    return x*f;
}
int main()
{
	do
	{
		n=read(),W=read();
		if(n==-1&&W==-1) break;
		for(int i=0;i<=30;i++) obj[i].clear();
		memset(f,0,sizeof(f));
		memset(g,0,sizeof(g));
		for(int i=1,w,v;i<=n;i++)
		{
			w=read(),v=read();
			int cnt=0;
			while(!(w&1))
			{
				w>>=1;
				cnt++;
			}
			temp.num=w,temp.val=v;
			obj[cnt].push_back(temp);
		}
		for(int i=0;i<=30;i++)
		for(int j=0;j<obj[i].size();j++)
		for(int k=min(W/(1LL<<i),(ll)10*n);k>=obj[i][j].num;k--)
		f[i][k]=max(f[i][k],f[i][k-obj[i][j].num]+obj[i][j].val);
		for(int j=0;j<=10*n;j++) g[0][j]=f[0][j];
		for(int i=1;i<=31;i++)
		for(int j=min((ll)10*n,W/(1LL<<i));j>=0;j--)
		for(int k=0;k<=j;k++)
		g[i][j]=max(g[i][j],f[i][k]+g[i-1][(j-k<<1)+((W>>i-1)&1)]);
		printf("%lld\n",g[31][0]);
	}while(1);
	return 0;
}
posted @ 2021-09-21 21:29  最爱丁珰  阅读(33)  评论(0编辑  收藏  举报