题解报告——梦幻岛的珠宝

传送门

题目描述

给你N颗宝石,每颗宝石都有重量和价值。要你从这些宝石中选取一些宝石,保证总重量不超过W,且总价值最大为,并输出最大的总价值。数据范围:N<=100;W<=2^30,并且保证每颗宝石的重量符合a*2^b(a<=10;b<=30)

输入输出格式

输入格式:

输入文件中包含多组数据。每组数据的格式如下:第一行是两个正整数n和W,1<=n<=100,1<=W<=2^30,分别表示宝石的数目和最多能带走的宝石重量。接下来的n行,每行有两个正整数weighti和valuei,1<=weighti<=2^30, 0<=valuei<=2^30,分别表示第i颗宝石的重量和价值,且保证weighti能写成a*2^b(1<=a<=10,0<=b<=30)的形式。同一行的两个正整数之间用空格隔开。最后一组数据的后面有两个-1,表示文件的结束。这两个-1并不代表一组数据,你不需对这组数据输出结果。并且输入文件中数据的组数不超过20。

输出格式:

对于输入的每组数据,输出一个整数C,表示小P最多能带走的宝石的总价值。每个结果整数C单独占一行,且保证C不会超过2^30。

输入输出样例

输入样例#1:
4 10
8 9
5 8
4 6
2 5
4 13
8 9
5 8
4 6
2 5
16 75594681
393216 5533
2 77
32768 467
29360128 407840
112 68
24576 372
768 60
33554432 466099
16384 318
33554432 466090
2048 111
24576 350
9216 216
12582912 174768
16384 295
1024 76
-1 -1
输出样例#1:
14
19
1050650

【思路分析】
首先看到这道题我们就会发现这是一个背包,然后看到数据范围发现这显然不是很资瓷背包的操作啊,最后发现正解其实还真的就是背包。
这里我们使用的不是普通的背包,美其名曰分层背包。
看到了每个物品的重量都可以表示为j*2^i,我们就可以按进制分层,每一层都是i相同的,然后我们先层内做一个普通的背包,然后再跨层做背包。
用dp[i][j]表示第i层系数为j,也就是j*2^i重量的最大价值,然后根据j来进行层内dp。
注意我们只需要维护最大价值即可,所以不用像平时做背包时那样判断当前体积出现过没有,直接加上新增体积即可。
然后发现这样做背包有个妙妙的性质那就是背包中体积越大的地方价值也越大,可以自行脑补。
然后就做跨层背包,由于上面的性质,我们设dp[i][j]表示j*2^i+w&((1<<i)-1)的体积,然后转移即可

【代码实现】
 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cctype>
 4 #include<cstring>
 5 using namespace std;
 6 typedef long long ll;
 7 void read(int &v)
 8 {
 9     int f;char ch;
10     while(!isdigit(ch=getchar())&&ch!='-'); ch=='-'?(v=0,f=-1):(v=ch-'0',f=1);
11     while(isdigit(ch=getchar())) v=v*10+ch-'0';v=v*f;
12 }
13 const int N=35;
14 int n,m,val,exp;
15 ll dp[N][1005];
16 int main()
17 {
18     while(1)
19     {
20         memset(dp,0,sizeof(dp));
21         read(n),read(m);
22         if(n==-1&&m==-1) break;
23         for(int i=1;i<=n;i++)
24         {
25             read(exp),read(val);
26             int cnt=0;
27             while(exp%2==0) exp=exp>>1,cnt++;
28             for(int j=500;j>=exp;j--) dp[cnt][j]=max(dp[cnt][j],dp[cnt][j-exp]+val);
29         }
30         int top=0;
31         for(int i=m;i;i>>=1) top++;top--;
32         for(int i=1;i<=top;i++)
33         for(int j=500;j>=0;j--)
34         for(int k=0;k<=j;k++)
35         dp[i][j]=max(dp[i][j],dp[i][j-k]+dp[i-1][min(500,(2*k)|(m>>(i-1))&1)]);
36         printf("%lld
",dp[top][1]);
37     }
38     return 0;
39 }

 
原文地址:https://www.cnblogs.com/genius777/p/9571511.html