DP之背包

一.01背包:

(以下均可用一维来写

即只能选择一次的物品装在一定容积的背包中。f[i][j]表示前i件物品在容积为j时的最大价值。

for(int i = 1; i <= n ;  i++){

  for(int j = v ; j >= 0 ; j--){

    if (w[i]<=j )

       f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+c[i]);

    else

      f[i][j]=f[i-1][v];

   }

}

有需要注意的的地方:

我们看到的求最优解的背包问题题目中,事实上有两种不太相同的问法。有的题目要求“恰好装满背包”时的最优解,有的题目则并没有要求必须把背包装满。一种区别这两种问法的实现方法是在初始化的时候有所不同。
如果是第一种问法,要求恰好装满背包,那么在初始化时除了 F[0] 为 0其它F[1~V ] 均设为 - ,这样就可以保证最终得到的 F[V ] 是一种恰好装满背包的最优解。
如果并没有要求必须把背包装满,而是只希望价格尽量大,初始化时应该将 F[0~V ]全部设为 0
这是为什么呢?可以这样理解:初始化的 F 数组事实上就是在没有任何物品可以放入背包时的合法状态。如果要求背包恰好装满,那么此时只有容量为 0 的背包可以在什么也不装且价值为 0 的情况下被“恰好装满”,其它容量的背包均没有合法的解,属于未定义的状态,应该被赋值为 - 了。如果背包并非必须被装满,那么任何容量的背包都有一个合法解“什么都不装”,这个解的价值为 0,所以初始时状态的值也就全部为 0了。
(建议自己先画一张表,手动模拟一下状态转移的过程--这是非恰好的情况
过程建议自己亲自手模
i物品(体积,价值)\j容积 0 1 2 3 4 5
1(2,3) 0 2 3 5 6 7
2(1,2) 0 2 2 4 6 6
3(3,4)  0 0 2 2 2 2
4(2,2) 0 0 0 0 0 0
我自己的理解则是:
因为要求是刚好装满,所以它的容积必须在之前已经有的状态上来转移。我觉得从开始的情况来理解比较容易

if (w[i]<=j )

       f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+c[i]);

    else

      f[i][j]=f[i-1][v];

----------------------------------------------------------------------------不得不说,想要说明白真的好难,自己理解有限,表达能力也差,只能说个半解吧

数据与代码如上,f[1][j] <-- f[0][j]。f[1][j]只能通过f[0][j]的状态转移过来,要么是直接继承f[0][j]的状态,也可以通过f[0][j-w[1]](此处即f[0][0~v-w[i]])的转移,也就是说只有当容积j恰好为w[1]的时候,才会使得f[i][j]的值能够得到有效的更新----因为初始化时只有f[0][0]的值为0,其他的均为负无穷,即使加也还是小的不得了。以此往后进行状态转移,只有当当前的容积状态的确能和之前的衔接上来才会得到有效的更新。因为当初始值不是负无穷时,受物品体积数大小的限制,并非所有的体积状态都能组合成。
而当并非恰好时,则并不需要一定要求当前的体积大小状态一定在之前已有基础(即前面的体积可以组合成的数)上才能进行有效的转移--反正都是0。

二.完全背包:

即一件物品可以无数次选择。

可以和01背包的思想结合,即将一件物品拆成多件(虽说是无数次选择,但毕竟背包容积有限)

仍然可以按照01背包的思想来解决:令f[i][v]表示前i个物品放入容积为v背包最大权值

伪代码:

for i=1..N

for v=0..V//不同于01背包①

f[v]=max(f[v],f[v-w[i]]+c[i])(v>=w[i],1<=i<=n)/ /补充:二维时还有区别----F[i, v] = max(F[i  -1, v],F[  i ,v - Ci] +Wi) ②

解释:①因为可以在之前的状态上进行转移,所以先找到之前的状态0-->n
②不同于01背包i-1这是i就是说选择放这件物品的同时也可以在这件物品的之前容积状态基础上继承。
posted @ 2019-11-14 17:21  beca~se  阅读(...)  评论(... 编辑 收藏