bzoj1190 梦幻岛宝珠

题目链接:https://darkbzoj.tk/problem/1190

显然不可能直接0/1背包
考虑(wei = a * 2 ^ b)这个性质,于是可以按(b)分层转移,
(f[i][j])表示只用(wei = a * 2 ^ i)的物品拼成重量为(j * 2 ^ i)的最大价值
(f[i][j])即为普通的0/1背包
考虑分层转移
(g[i][j])表示只用(wei = a * 2 ^ k,(k = 1,2,ldots,i))的物品拼成重量为(j * 2 ^ i + (w的后i-1位))的最大价值
(g[i][j])的转移方程为

[g[i][j] = max(f[i][j-k] + g[i-1][min(1000,g[i-1][k*2 + w_{i-1}]),(k = 0,1,ldots,j) ]

最终的答案即为(g[len][1])

遗留问题:0/1背包,完全背包等的初值问题

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<stack>
#include<queue>
using namespace std;
typedef long long ll;

const int maxn = 110;

int n,m,cnt;
ll v[maxn],w[maxn],c[maxn],vb[maxn][maxn],f[maxn][maxn*100],g[maxn][maxn*100];
int wb[maxn][maxn];

ll read(){ ll s=0,f=1; char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ s=s*10+ch-'0'; ch=getchar(); } return s*f; }

int main(){
	while(1){
		memset(c,0,sizeof(c));
		memset(f,0,sizeof(f));
		memset(g,0,sizeof(g)); 
		 
		n = read(), m = read(); cnt = 0;
		if(n==-1 && m==-1) break;
		
		for(int i=1;i<=n;++i) w[i] = read(), v[i] = read();
		
		int W = m, num;
		while(W > 0){ ++cnt; W >>= 1; }
		
		for(int i=1;i<=n;++i){
			W = w[i], num = 0;
			while(W % 2 == 0){ ++num; W /= 2; }
			wb[num][++c[num]] = W, vb[num][c[num]] = v[i];
		}
		
		for(int i=0;i<cnt;++i){
			f[i][0] = 0;
			for(int I=1;I<=c[i];++I){
				for(int j=1000;j>=wb[i][I];--j){
					if(f[i][j-wb[i][I]] != -1) f[i][j] = max(f[i][j], f[i][j-wb[i][I]] + vb[i][I]);
				}
			}
		}
		
		for(int j=0;j<=1000;++j) g[0][j] = f[0][j];
		for(int i=1;i<cnt;++i){
			for(int j=0;j<=1000;++j){
				for(int k=0;k<=j;++k){
					if(((k<<1) + ((m>>(i-1)) & 1)) <= 1000) g[i][j] = max(g[i][j], f[i][j-k] + g[i-1][(k<<1) + ((m>>(i-1)) & 1)]);
				}
			}
		}
		
		printf("%lld
",g[cnt-1][1]);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/tuchen/p/13869333.html