P3188
[HNOI2007]梦幻岛宝珠
输入
普通的 01 背包,但是数据很大,不过给了写特殊性质,
然后很容易就可以想到按这个
然后转移很好搞:
最重要的是如何整合这几组状态。
反正肯定不能直接容量定义状态,那不如就照葫芦画瓢,同样按上面的分组转移。
定义状态
显然我们不知道这个状态的定义到底有何意义,仍然没有合并的思路。
那么不加解释地给出转移方程:
首先,我们知道,当前组的状态的费用乘上 2 其实就可以应用到上一组的状态再转移过来,这个很好想。但是为什么会加上
所以最后的
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
const ll N=1e2;
ll n,W;
ll v[N+5],w[N+5],a[N+5];
ll f[40][N*40+5],g[40][N*40+5];
ll count(ll x) {
ll res=0;while(x>0) {x>>=1;res++;}return res;
}
inline ll read() {
ll ret=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-f;ch=getchar();}
while(ch>='0'&&ch<='9') {ret=(ret<<3)+(ret<<1)+ch-'0';ch=getchar();}
return ret*f;
}
void write(ll x) {
static char buf[22];static ll len=-1;
if(x>=0) {
do{buf[++len]=x%10+48;x/=10;}while(x);
}
else {
putchar('-');
do{buf[++len]=-(x%10)+48;x/=10;}while(x);
}
while(len>=0) putchar(buf[len--]);
}
void writeln(ll x) {
write(x);putchar('\n');
}
int main() {
while(1) {
memset(f,0,sizeof(f));memset(g,0,sizeof(g));
n=read();W=read();
if(n==-1&&W==-1) break;
for(ll i=1;i<=n;i++) {
w[i]=read();v[i]=read();
ll tmp_lowbit=w[i]&(-w[i]);
a[i]=w[i]/tmp_lowbit;
ll tmp_pos=count(tmp_lowbit);
for(ll j=400;j>=a[i];j--) {
f[tmp_pos][j]=max(f[tmp_pos][j],f[tmp_pos][j-a[i]]+v[i]);
}
}
ll tmp_cntw=count(W);
for(ll i=1;i<=tmp_cntw;i++) {
for(ll j=400;j>=0;j--) {
for(ll k=0;k<=j;k++) {
g[i][j]=max(g[i][j],g[i-1][(j-k)*2+((W>>(i-2))&1)]+f[i][k]);
}
}
}
writeln(g[tmp_cntw][1]);
}
return 0;
}