日志文章

2007年08月23日 22:18:38

金明的预算方案——noip06提高解题报告

金明的预算方案(ruiqi)

  如果看过普及组试卷就会发现,对应的第二个题目,也是一个样的背景,提高组只是多了个“主件附件”的的关系,如果去掉这一点,就全没区别了。也就成了经典的背包问题了。也就是多了这么一点,考试的时候就晕了。不知道怎么做了。后来才发现是个很简单的dp题目。可惜我当时没做出来。
  草率的审题,可能会得到这样的算法:dp,对每一个物品做两种决策,取与不取。如果取,满足两个条件:1.要么它是主件,要么它所属的主件已经在包里了。2.放进去后的重要度与价格的成绩的总和要比没放进时的大。这两个条件缺一不可的。于是呼,得到如下的动规方程:
   f[i,j]:=f[i-1,j];
if (i为主件or i的附件在包中)and (f[i,j]<f[i,j-v]+v*w)
then f[i,j]:=f[i,j-v]+v*w;
  我们来分析一下复杂度,空间:dp的阶段为n^2,对与每一个阶段都要记录该状态下在包中的物品有哪些(因为要确定附件的主件是否在包中),每个阶段的记录都要O(n)的空间,所以总的就是O(n^3)。时间,一个dpn^2的外层循环,内部用布尔量加个主附件的对应数组,为O(1),和起来就为O(n^2)的复杂度。
  可以看的出,时间的需求为32000*60,不成问题。空间32000*60*60,大约要7.5M的空间,在64M的要求下是完全可以的过的。如果用上题目中的一个很隐秘的条件:“每件物品都是10元的整数倍”,就可以把速度在提高十倍。


  细细的看题目,还一个很重要的条件我们还没用:“每个主件可以有0个,1个或2个附件”。这貌似不起眼的一句话,却给我们降低复杂度提供了条件。想一想,为什么题目要对附件的个数做限制呢,明显是在降低难度。

  对于一套物品(包含主件,所以的附件),我们称为一个属类,对一个属类的物品的购买方法,有以下5种:
    1.一个都不买
    2.主件
    3.主件+附件1
    4.主件+附件2
    5.主件+附件1+附件2
  这五种购买方法也是唯一的五种方法,也就是说对一属类的物品,我们只有上述的5种购买方法。
  于是我们很自然的就会想到把物品按物品的属类捆在一起考虑。这样我们把物品的属类作为dp的状态。可以得到如下的dp方程:
  f[i,j]=max{f[i-1,j];
  f[i-1,j-v[i,0]]+v[i,0]*w[i,0];
  f[i-1,j-v[i,0]-v[i,1]]+v[i,0]*w[i,0]+v[i,1]*w[i,1];
  f[i-1,j-v[i,0]-v[i,2]]+v[i,0]*w[i,0]+v[i,2]*w[i,2];
  f[i-1,j-v[i,0]-v[i,1]-v[i,2]]+v[i,0]*w[i,0]+v[i,1]*w[i,1]+v[i,2]*w[i,2];}
很显然时间复杂度为O(n^2),空间复杂度为O(n^2),加上利用“每件物品都是10元的整数倍”除以10的优化,本题就很完美的解决了。


附程序:

program Qi(input,output);
type
node=record
  u:integer;
  v:array[0..2] of integer;
  p:array[0..2] of integer;
  end;
var
n,m,k:integer;
w:array[1..60] of node;
f:array[0..60,0..3200] of longint;
g:array[1..60] of integer;
procedure init;
var
i,j:integer;
vx,px,qx:array[1..60] of integer;
begin
readln(n,m); k:=0;
for i:=1 to m do
begin
  readln(vx,px,qx);
  if qx=0
  then begin
    k:=k+1; g:=k;
    with w[k] do
    begin
    u:=0;
    v[0]:=vx; p[0]:=px;
    for j:=1 to 2 do begin v[j]:=0; p[j]:=0; end;
    end;
    end;
end;
for i:=1 to m do
if qx<>0
  then begin
    with w[g[qx]] do
    begin
    u:=u+1;
    v:=vx; p:=px;
    end;
    end;
for i:=1 to k do
with w do
  begin
  for j:=0 to 2 do write('(',v[j],',',p[j],') ');
  writeln;
  end;
end;
procedure doit;
var
i,j:integer;
begin
fillchar(f,sizeof(f),0);
for i:=1 to k do
with w do
  begin
  for j:=1 to n do
  begin
    f[i,j]:=f[i-1,j];
    if (j>=v[0]) and (f[i,j]<f[i-1,j-v[0]]+v[0]*p[0])
    then f[i,j]:=f[i-1,j-v[0]]+v[0]*p[0];
    if (j>=v[0]+v[1]) and (f[i,j]<f[i-1,j-v[0]-v[1]]+v[0]*p[0]+v[1]*p[1])
    then f[i,j]:=f[i-1,j-v[0]-v[1]]+v[0]*p[0]+v[1]*p[1];
    if (j>=v[0]+v[2]) and (f[i,j]<f[i-1,j-v[0]-v[2]]+v[0]*p[0]+v[2]*p[2])
    then f[i,j]:=f[i-1,j-v[0]-v[2]]+v[0]*p[0]+v[2]*p[2];
    if (j>=v[0]+v[1]+v[2]) and (f[i,j]<f[i-1,j-v[0]-v[1]-v[2]]+v[0]*p[0]+v[1]*p[1]+v[2]*p[2])
    then f[i,j]:=f[i-1,j-v[0]-v[1]-v[2]]+v[0]*p[0]+v[1]*p[1]+v[2]*p[2];
  end;
  end;
end;
procedure print;
begin
writeln(f[k,n]);
end;
begin
init;
doit;
print;
end.


                                                                ruiqi说:谢谢牛们的支持!


附件: 金明的预算方案.rar (5 K)

类别: 原创解题报告 |  评论(3) |  浏览(2936) |  收藏
一共有 3 条评论
3楼 [匿名]475802985 2008年10月26日 16:46:56 Says:
我也是这么想的,但写出来就40分,郁闷
2楼 [楼主]ruiqi 的小小sky 2007年11月26日 15:44:04 Says:
orz~~

只能说不是一种标准的算法吧~~
1楼 [匿名]alpha.beta 2007年11月26日 14:24:52 Says:
第一种方法是错的
发表评论
看不清楚,换一张