您的位置:首页 > 其它

动态规划专栏(3)-背包问题(1)

2017-11-05 20:48 232 查看
前言:

动态规划由于题目的难易、类型,被分成四大类型:树形动态规划、区间形动态规划、背包形动态规划和线性动态规划四大类,而其中最具代表性的非背包莫属,他具有各种变形,以下的这类背包文章会告诉你,背包如何去解。

(1)、0/1背包

                                 旅行者的背包(pack.Pas)

题目描述

一个旅行者有一个最多能装m公斤物品的背包,现在有n件物品,它们的重量分别是w1,w2,…,wn,它们的价值分别为c1,c2,…,cn。若
4000
每件物品只有一件,求旅行者能获得的最大总价值。

输入

第一行:两个整数,m(背包容量,m<=200)和n(物品数量,n<=30)。

第二~n+1行:每行两个整数wi,ci,表示每个物品的重量和价值。

输出

一个数据,表示最大总价值。

 

思想:有一些人,会以为这种题目是贪心,他的理由是:性价比嘛!每次拿最大!不过只要性价比最优的那组在整一个方案里并不是最优的,那么贪心就不能成立。然后有些人会果断选分治,理由:条件明确。在这里,我承认分治没有错,那么,既然是一个正确算法,我就给大家理一理思路。

分治:

原问题:重量为M的背包价值为V,正好可以装下

子问题1:重量为M-w[i]的背包价值为V+v[i],正好可以装下(m≥w[i])

子问题2:重量为M-w[i]的背包价值为V,正好可以装下(m≥w[i])

基本到底情况:if m=0返回(T)

              If v=0
返回(F)

最后求出max{M}就行了。

但是我们发现,可以把参数放进f[][]更加节约,f[i,j]代表重量为I,价值为J的方案的最优策略,而每一种物品最多只能选一个,有这些特征的动态规划叫做01背包。于是,一个二维的动态规划就这么推断出来了。

公式

   F[i,j]:=max{f[i,j],f[i-w[i],v[j]+j]};//不取w[i]的价值

为了更好的理解二维的模式,我们来列表:

假设容量为4,每一个物品都是1,1

 

0

1

2

3

4(w[i])

0

1

0

0

0

0

1

0

1

1

1

0

2

0

0

1

2

0

3

0

0

0

3

0

4(v[i])

0

0

0

0

4(答案)

 

(1.1)一维0/1背包

从上面我们可以发现一个对空间上的优化,我们不妨去掉价值,加入答案之中去,

最简化公式:

F[j]:=max{f[j],f[j-w[i]]+v[i]};

这样可以大大减少了对空间的要求。

不过值得注意的是,重量循环枚举一定要downto,否则会有后效性(也就是说,就变成了后面要讲的完全背包,更新了不变的值)

(1.2)变形-0/1背包

                  装箱问题(question.pas)

题目描述

有一个箱子容量为v(正整数,0<=v<=20000),同时有n个物品(0<n<=30),每个物品有一个体积(正整数)。

要求从n个物品中,任取若干个装入箱内,使箱子的剩余空间为最小。

 

输入

箱子的容量v

物品数n

接下来n行,分别表示这n个物品的体积

 

输出

箱子剩余空间

 

思想:在做这一道题时,我们会发现一个问题,这道题完全吻合01背包的特征,但是却没有价值,求得的要是剩余。不过,我们再仔细想想,这道题的价值貌似就是重量,要的是剩余,不就是要求和-最优价值(重量)吗?这是一道变形01背包。套公式:

  for i:=1 to m do

  begin

    readln(w[i]);

    c[i]:=w[i];//重量就是价值

  end;

  for i:=1 to m do

    for j:=n downto w[i] do

    if f[j-w[i]]+c[i]>f[j] then

      f[j]:=f[j-w[i]]+c[i];

  writeln(n-f
);//最小剩余空间=总空间-最大放置空间

 

(2)、完全背包

                   平方数(sqr.pas)  

题目描述

珍珍学习乘法时,发现4=2*2,9=3*3,…,而2不可能分解为二个整数的乘积,但可以分解为1*1+1*1。她想知道对任意的正整数n,把它分解为几个整数与自身相乘之和,有多少种方案呢?

 

输入

只有一行,该行只有一个正整数n。

 

输出

只有一行,该行只有一个正整数,表示总方案数。                 

 

思想:这道题在我以前写的博客里(动态规划专栏(1))中出现过这道题,三重循环土生土长的动态规划,不过,这一次,我只要二重循环。可以发现,这道题区别于01背包的是,他每一个数(k^2)都可以取无数种,而且在一维中有后效性,有这个特征的,我们把它叫做

完全背包,他与01背包就只有一个差距,01的downto变成了有后效性的to(每一个物品无限种,更新了不变的值没有关系),而且在这道题中f[j]代表重量为J的物品,方案种数为f[j]。公式:

For j:=w[i] to m do

  F[j]=max{f[j],f[j-w[i]]+c[i]]};(跟个数无关)

求个数:

For j:=w[i] to m do

  F[j]=max{f[j],f[j-w[i]]};

 

运行后比三重循环将近快了10倍!!

 

终极代码共借鉴:

For i:=1 to n do

  For j:=w[i] to m do

F[j]:=max(f[j],f[j-w[i]]);

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: