背包问题的优化(洛谷1776 宝物筛选_NOI导刊)

 

背包型dp,但是没有看清数据范围差点认为是水题了,(然后诡异的拿了20分)
标解是:2进制优化,比较简单把每一类物品看做若干个相互独立的物品,放在一个另外的数组里,然后全局跑一边01就可以。
主要思想是:将一种物品分成1、2、3..一份,然后跑01(01的复杂度低啊!)
如果难以理解的话不妨举个例子:如2=1+1;5=1+2+2;4=1+2+1;
就是说依次递增,当最后一次的次数太大了使前面所有的和加起来大于次数了那么我们就不继续递增拉
好理解的程序的话就是这样的但是跑的慢(最后一个点500多ms!)

uses math;
var n,v,i,j,tc,tw,tt,cnt,pp:longint;
    w,c:array[0..1000000]of longint;
    f:array[0..1000000]of longint;
begin
 readln(n,v);
 cnt:=1;
 for i:=1 to n do begin
  readln(tc,tw,tt);//读入一种物品的价值、代价、次数
  pp:=1;//分解的第一种是1个分解
  while true do begin//死循环拉~~
   c[cnt]:=pp*tc;//加1个物品的物品
   w[cnt]:=pp*tw;
   inc(cnt);
   dec(tt,pp);//减去次数
   inc(pp);//下一种次数
   if tt-pp<0 then break;//不够了。。。
  end;
  if tt>0 then begin//如果还有剩余次数那么重新来一个把!
   c[cnt]:=tt*tc;
   w[cnt]:=tt*tw;
   inc(cnt);
  end;
 end;
 for i:=1 to cnt do//跑一组01就完事了;
  for j:=v downto w[i] do
   f[j]:=max(f[j],f[j-w[i]]+c[i]);
 writeln(f[v]);
end.//收工!


(虽然上面是AC程序但是时间效率太低最后一点500ms,其实上面我们是分解123,但是现在可以按照二进制来做124)

其实这道题的本质是二进制的算法,我们考虑把这个物品换成若干件物品,使得原问题中不论这种物品取多少件(0到最大件数p之间),都能等价于取若干件代换以后的物品。且超过x件的策略必定不能出现。就是说,将每个物品分成若干件01背包中的物品,其中每件物品有一个系数。这件物品的费用和价值均是原来的费用和价值乘以这个系数。(假设有一种价值为v,重量为w,限购次数为x的物品)令这些系数分别为1,2,2^2,…,2^(k-1),x+1-2^k,且k是满足x+1-2^k>0的最大整数。由于加了位运算,解题的能力快速提高,时间比较快(虽然上面是可以AC但是下面更加优秀)

uses math;
var n,v,i,j,aa,cc,bb,cnt:longint;
    w,c:array[0..1000000]of longint;
    f:array[0..1000000]of longint;
begin
 readln(n,V);//读入物品和背包体积
 cnt:=1;//分裂后待选的物品总数
 for i:=1 to n do begin
  readln(aa,bb,cc);//每一件物品的价值、体积、次数
  j:=1;
  while j<=cc do begin//分成若干个物品
   w[cnt]:=j*bb;//第cnt件物品的体积=体积*次数
   c[cnt]:=j*aa;//第cnt件物品的价值=价值*次数
   cc:=cc-j;//次数分解
   j:=j<<1;//j乘以2
   inc(cnt);
  end;
  if cc>0 then begin//如果次数还有多余那么一次性加
   w[cnt]:=cc*bb;
   c[cnt]:=cc*aa;
   inc(cnt);
  end;
 end;
 for i:=1 to cnt do
  for j:=v downto w[i] do
   f[j]:=max(f[j],f[j-w[i]]+c[i]);
 writeln(f[V]);
end.

 

转载于:https://www.cnblogs.com/ljc20020730/p/7404296.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值