给你N颗宝石,每颗宝石都有重量和价值。要你从这些宝石中选取一些宝石,保证总重量不超过W,且总价值最大为
,并输出最大的总价值。
数据范围:
N
<
=
100
;
W
<
=
2
30
N<=100;W<=2^{30}
N<=100;W<=230,并且保证每颗宝石的重量符合
a
∗
2
b
(
a
<
=
10
,
b
<
=
30
)
a*2^b(a<=10,b<=30)
a∗2b(a<=10,b<=30)
题解:
这个题。
其实所有的背包都可以把每个物品的重量变成
a
∗
2
b
a*2^b
a∗2b,如果有什么只基于二进制的高明解法那么就可以去拿图灵奖了。
但是这个题的特殊点就是
a
<
=
10
a<=10
a<=10。
但是还是很难。
如果
a
=
0
o
r
1
a=0\ or \ 1
a=0 or 1
那么这就是一个数位DP(还是
O
(
n
)
O(n)
O(n)的)。
a
<
=
10
a<=10
a<=10时仍然考虑从高位到低位的DP。
f
i
,
j
f_{i,j}
fi,j表示剩余的容量在
j
2
i
j2^i
j2i到
(
j
+
1
)
2
i
−
1
(j+1)2^i-1
(j+1)2i−1之间的最大价值和。
为什么是之间,因为之下的价值还没有定,也不用定,因为这个和当前层的任何物品能否放进去都没有关系,也就是说之下的价值因为没有存的价值所以我们就不存。
然后在一层层往低位转移的时候按总容量在这一位是1还是0来确定(清晰)容量的这一位。
这就是分层DP。
在信息对于每一层的需求量是逐层递增的时候,对于信息选择性忽视。
但是这并没有什么用,因为背包问题是NPC问题,复杂度指数增长的本性并没有更改。
这时我们回想
a
<
=
10
a<=10
a<=10有什么用。
所有
a
a
a的和最大为
10
n
=
1000
10n = 1000
10n=1000
我们DP时
j
2
i
j2^i
j2i会转移到
(
j
−
a
)
2
i
(j-a)2^i
(j−a)2i。
那么当
j
>
=
1000
j>=1000
j>=1000时,我们可以知道它永远不会转移到负数,也就是背包永不会被填满。
那么
j
>
=
1000
j>=1000
j>=1000时就按照
j
=
1000
j = 1000
j=1000来计算也可以得到正确的答案,反正都填不满。
那么复杂度就是
O
(
1000
∗
30
n
)
O(1000 * 30n)
O(1000∗30n)
普通的背包问题也是有了一个比较小的上界才可以有高效算法的。
不冤。
A C C o d e AC\ Code AC Code
#include<bits/stdc++.h>
#define pb push_back
using namespace std;
int n,L,f[32][1005];
void chk(int &a,int b){
a = max(a , b);
}
int main(){
while(~scanf("%d%d",&n,&L) && n!=-1){
vector<int>v[31],w[31];
for(int i=1,a,b;i<=n;i++){
scanf("%d%d",&a,&b);
int t = 0;
for(;a%2==0;a>>=1) t++;
v[t].pb(b) , w[t].pb(a);
}
int T=0;
memset(f,-0x3f,sizeof f);
f[31][0] = 0;
for(int i=30;i>=0;i--){
for(int j=0;j<=1000;j++)
if(f[i+1][j] >= 0)
chk(f[i][min(j<<1|(L>>i&1),1000)] , f[i+1][j]);
for(int j=0;j<v[i].size();j++){
for(int k=0;k<=1000;k++)
chk(f[i][k],f[i][min(k+w[i][j],1000)] + v[i][j]);
}
}
printf("%d\n",*max_element(f[0],f[0]+1001));
}
}