题目描述:
给你N颗宝石,每颗宝石都有重量和价值。要你从这些宝石中选取一些宝石,保证总重量不超过W,且总价值最大为,并输出最大的总价值。
N<=100;W<=230,并且保证每颗宝石的重量符合a*2b(a<=10;b<=30)
题目分析:
按照 b b b对物品分层,设 f [ i ] [ j ] f[i][j] f[i][j]表示可用容量为 j ∗ 2 i j*2^i j∗2i时已经装的最大价值。每一层继承上一层的背包,可用容量变为 j < < 1 ∣ ( W > > i & 1 ) j<<1|(W>>i\&1) j<<1∣(W>>i&1)。
如果由高位到低位做背包,那么容量是不需要超过1000*2b的,因为后面的所有物品加起来都没有这么重,所以如果剩余容量大于1000*2b,直接存在1000的位置就好了。
所以背包时每层的容量只需要开到1000,复杂度为
O
(
1000
∗
l
o
g
2
30
+
100
∗
1000
)
O(1000*log2^{30}+100*1000)
O(1000∗log230+100∗1000)
Code:
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
#define maxn 105
#define maxm 1005
using namespace std;
const int Log = 30, M = 1000;
int n,W,f[Log+3][maxm];
vector<int>wt[Log+1],val[Log+1];
inline void chkmax(int &a,int b){a<b&&(a=b);}
int main()
{
while(scanf("%d%d",&n,&W),n!=-1){
for(int i=0;i<=Log;i++) wt[i].clear(),val[i].clear();
for(int i=1,a,b,v;i<=n;i++){
scanf("%d%d",&a,&v),b=0;
while(!(a&1)) a>>=1,b++;
wt[b].push_back(a),val[b].push_back(v);
}
memset(f,-0x3f,sizeof f),f[Log+1][0]=0;
for(int i=Log;i>=0;i--){
for(int j=0;j<=M;j++)
chkmax(f[i][min(M,j<<1|(W>>i&1))],f[i+1][j]);
for(int j=wt[i].size()-1;j>=0;j--)
for(int k=0;k<=M-wt[i][j];k++)
chkmax(f[i][k],f[i][k+wt[i][j]]+val[i][j]);//qia hao
}
printf("%d\n",*max_element(f[0],f[0]+M+1));
}
}