题目链接: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;
}