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\n",g[cnt-1][1]);
}
return 0;
}