动态规划之01背包详解
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动态规划之01背包详解相关的知识,希望对你有一定的参考价值。
先看问题:
有N件物品和一个容量为V的背包。(每种物品均只有一件)第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。
通过阅读问题,因为背包就是要往里面放东西,所以一件物品就是有放或不放两种情况,那么怎么才能判断当前物品该不该放进去从而使利益最大化呢。
首先,我们用i代表前i件物品,v代表包的最大承重,ci是第i件物品的重量、wi是第i件物品的价值、f[i,j]是最大价值(i个物品放入有j个空间的包)。
第一种情况:第i件不放进去,这时所得价值(f[i][j])为:f[i][j]=f[i-1][v]
因为不放进去,所以说当前价值(f[i][j])为前i-1个物品的价值f[i-1][v]:当前i个物品用j个空间所拥有的价值就等于前i-1个物品用j个空间的价值,因为物品i没有放进去,
所以就相当于继承f[i-1][v]了,所以f[i][j]=f[i-1][v]
另一种情况:第i件放进去,这时所得价值为:f[i][j]=f[i-1][v-c[i]]+w[i]
因为放进去了,所以说当前价值(f[i][j])不是前i-1个物品的价值f[i-1][v]:因为你放进去了,所以这个时候包内的空间就不是v了,而应该是v-c[i],所以说,
你现在需要继承前i-1个物品占用体积为v-c[i]时的价值,因为你将物品放进去了,所以还需要加上当前物品价值wi,所以f[i][j]=f[i-1][v-c[i]]+w[i]
因为对于第i件物品就是这两种操作,而你又想要最大价值,所以
f[i][j]=max(f[i-1][v],f[i-1][v-c[i]]+w[i])
贴一段代码:(n为物品数量)
for (int i=1;i<=n;++i)
{ for (int j=v;j>=1;--j)
{ if(c[i]<=j)//如果当前物品可以放入当前空间的背包 f[i][j]=max(f[i-1][j],f[i-1][j-c[i]]+w[i]); else f[i][j]=f[i-1][j];//如果当前物品放不进去,那么继承前i个物品在当前空间大小时的价值 } }
当n=3,v=6时的表格:
c[1]=2 , w[1]=7 ; c[2]=3 , w[2]=1 ; c[3]=5 , w[3]=4;我用红色的数字标记一下在当前代码下填表的顺序,运行之后f[n][v]为最优值,可见此时最优值为f[3][6]=8;
i\j |
j=0 | j=1 | j=2 | j=3 | j=4 | j=5 | j=6 | ||
i=0 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | ||
i=1 |
0 | (6)7 | (5)7 | (4)7 | (3)7 | (2)7 | (1)7 | ||
i=2 |
0 | (12)8 | (11)8 | (10)8 | (9)8 | (8)8 | (7)8 | ||
i=3 |
0 | (18)0 | (17)0 | (16)0 | (15)0 | (14)8 | (13)8 |
以上是用二维数组存储的,其实还可以用一维数组存储进行空间优化(滚动数组)
从上面计算f[i][j]可以看出,在计算f[i][j]时只使用了f[i-1][0……j],所以说并没有使用其他子问题,所以说在存储子问题解的时候,只用存储f[i-1]的子问题解即可;所以说可以用一个一维数组替换掉那个二维数组,一个存储子问题,一个存储正在解决的子问题。
我们用f[v]表示当前状态是容量为v的背包所得价值
那滚动数组应当如何滚动呢,用二维数组计算f[i][j]时只使用了f[i-1][0……j],而并没有使用到f[i-1][j+1],所以在计算j的循环的时候,让j=M……1
这个时候你也许会问,那你上面用的二维数组不也是逆序吗,其实对于二维数组的来说,正逆序无所谓,当你懂得01背包的算法之后就可以明白这一点
下面给出一维数组优化过的代码:
for (int i=1;i<=n;++i)
{
for (int j=v;j>=0;--j)
{
if(t[i]<=j)
f[j]=max(f[j],f[j-t[i]]+t[i]);
else f[j]=f[j];
}
}
因为二维的我已经讲解的很详细了,所以一维数组的跟二维数组的相差的不是太多,只要你懂了二维数组的那个是如何工作的,这个我相信你也会很容易理解,我这里就不细化讲解了。
本文章原创,未经我允许不得转载
Authentic Author : Tranx
2017.10.1 21:11
以上是关于动态规划之01背包详解的主要内容,如果未能解决你的问题,请参考以下文章