BZOJ1190:[HNOI2007]梦幻岛宝石

Description

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

Input

输入文件中包含多组数据。每组数据的格式如下:第一行是两个正整数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。

Output

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

Sample Input

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

Sample Output

14
19
1050650
 

题解:

显然这是一个01背包,但是体积容积都特别大。

事实上,每颗宝石体积在2进制情况下的有效位数都特别有限,可以对体积按位dp。

F[i,j]表示当前DP到第i位,使用j*2^(i-1)体积可以得到的最大价值。对于i的阶段,只要用b=i-1的宝石去DP,然后进位转移到下一阶段。

因为a<=10,n<=100,j最多为1000。

注意考虑上界,需额外开一个数组表示所考虑有效位数之后的数都在W对应范围内的情况。

代码:

 1 uses math;
 2 var
 3   i,j,k,l,n,m,ii,ww:longint;
 4   tot,w:int64;
 5   a:array[0..1,0..40,0..1001]of int64;
 6   s:array[0..40]of longint;
 7   b:array[0..101,0..2]of int64;
 8   t:array[0..2]of int64;
 9 begin
10   while true do
11   begin
12     readln(n,w); tot:=0;
13     //w:=50000;
14     if n=-1 then break;
15     for i:=1 to n do
16     begin
17       readln(b[i,1],b[i,0]);
18       b[i,2]:=1;
19       while b[i,1] mod 2=0 do begin b[i,1]:=b[i,1] div 2; inc(b[i,2]); end;
20     end;
21     for i:=1 to n-1 do
22     for j:=i+1 to n do
23     if b[i,2]>b[j,2] then begin t:=b[i]; b[i]:=b[j]; b[j]:=t; end;
24     fillchar(a,sizeof(a),0);
25     if n=-1 then break;
26     l:=0;
27     while w>0 do
28     begin
29       inc(l); s[l]:=w mod 2;
30       w:=w div 2;
31     end;
32     ii:=1;
33     for i:=1 to l do
34     begin
35       for j:=0 to 1000 do
36       if(s[i-1]=1)or(j mod 2=0)then
37       begin
38         a[0,i,j div 2]:=max(a[0,i,j div 2],a[0,i-1,j]);
39         a[0,i,j div 2]:=max(a[1,i,j div 2],a[0,i-1,j]);
40       end;
41       for j:=0 to 1000 do
42       begin
43         a[1,i,(j div 2)+1]:=max(a[1,i,(j div 2)+1],a[1,i-1,j]);
44         if j mod 2=0 then
45         a[1,i,j div 2]:=max(a[1,i,j div 2],a[1,i-1,j]);
46       end;
47       while(ii<=n)and(b[ii,2]<=i)do
48       begin
49         for j:=1000-b[ii,1] downto 0 do
50         begin
51           a[0,i,j+b[ii,1]]:=max(a[0,i,j+b[ii,1]],a[0,i,j]+b[ii,0]);
52           a[1,i,j+b[ii,1]]:=max(a[1,i,j+b[ii,1]],a[1,i,j]+b[ii,0]);
53         end;
54         inc(ii);
55       end;
56       for j:=1 to 1000 do begin a[0,i,j]:=max(a[0,i,j],a[0,i,j-1]);
57       a[1,i,j]:=max(a[1,i,j-1],a[1,i,j]); end;
58     end;
59     writeln(max(a[1,l,1],a[0,l,1]));
60   end;
61 end.
View Code
原文地址:https://www.cnblogs.com/GhostReach/p/6254898.html