题目描述
给你 N ( N ≤ 100 ) N(N\le 100) N(N≤100) 颗宝石,每颗宝石都有重量和价值。要你从这些宝石中选取一些宝石,保证总重量不超过 W ( W ≤ 2 30 ) W(W\le 2^{30}) W(W≤230),输出最大的总价值。保证每颗宝石的重量符合 a × 2 b ( a ≤ 10 , b ≤ 30 ) a\times 2^b(a\le 10,b\le30) a×2b(a≤10,b≤30)。
算法分析
设
f
[
i
]
[
j
]
f[i][j]
f[i][j] 表示背包容量为
j
×
2
i
+
W
&
(
(
1
<
<
i
)
−
1
)
j\times 2^i+W\&((1<<i)-1)
j×2i+W&((1<<i)−1) 时的最大总价值,
i
i
i 相同时的转移等同 0/1 背包,
i
i
i 不同时有
f
[
i
]
[
j
]
=
m
i
n
{
f
[
i
]
[
j
]
,
f
[
i
]
[
k
]
+
f
[
i
−
1
]
[
2
×
k
+
(
W
>
>
(
i
−
1
)
&
1
)
]
}
f[i][j]=min\{f[i][j],f[i][k]+f[i-1][2\times k+(W>>(i-1)\&1)]\}
f[i][j]=min{f[i][j],f[i][k]+f[i−1][2×k+(W>>(i−1)&1)]}。
我好菜啊
代码实现
#include <cstdio>
#include <cstring>
#include <algorithm>
#define UPD(x,y) x=std::max(x,y)
#define Min(x,y) std::min(x,y)
int n,w,f[35][1005];
int main() {
while(scanf("%d%d",&n,&w)==2&&((~n)||(~n))) {
memset(f,0,sizeof(f));
for(int i=0;i<n;++i) {
int a,b=0,v;
scanf("%d%d",&a,&v);
while(!(a&1)) {++b;a>>=1;}
for(int j=1000;j>=a;--j) UPD(f[b][j],f[b][j-a]+v);
}
int top=-1;for(int i=w;i;i>>=1) ++top;
for(int i=1;i<=top;++i) {
for(int j=1000;j>=0;--j) for(int k=0;k<=j;++k)
UPD(f[i][j],f[i][j-k]+f[i-1][Min(1000,(k<<1)|(w>>(i-1)&1))]);
}
printf("%d\n",f[top][1]);
}
return 0;
}