有n 个物品,它们有各自的重量和价值,现有给定容量的背包,
如何让背包里装入的物品具有最大的价值总和?
动态规划是将大问题分解成许多小问题,通过寻找大问题与小问
题之间的递推关系,解决一个一个小问题,最终达到解决原问题
的目的。通过填表将每个子问题的解记录下来,在新问题里需要
用到时可以直接提取,节约时间。
四个物体,背包容量为8.
i | 1 | 2 | 3 | 4 |
---|---|---|---|---|
w(体积) | 2 | 3 | 4 | 5 |
v(价值) | 3 | 4 | 5 | 6 |
解题过程:
1、创建一个新的变量a,a的值可以为1或0;
2、建立关系模型,max(v1a+v2a+v3a+v4a),a的值可以为1或0;
3、v1w1a+v2w2a+v3w3a+v4w4a<=背包容量,a的值可以为1或0;
4、本题有两个变量i(物品的个数),j(背包的容量);(i,j都是变化的)
5、那么定义V(i,j)为当前背包容量j,前i个物品最佳组合对应的价值
6、寻找递推关系,一件商品存在两种可能性:
第一,当前包的容量比该商品体积小,那么此时无法取到,即V(i,j)=V(i-1,j);
第二,当前包的容量比该商品大,但是取还是不取,当然是选择价值大的啦。
V(i,j)=max(V(i-1,j),V(i-1,j-w(i))+V(i));
7、创建二维列表依次遍历每一个物品就好了。
i/j | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 0 | 3 | 3 | 3 | 3 | 3 | 3 | 3 |
2 | 0 | 0 | 3 | 4 | 4 | 7 | 7 | 7 | 7 |
3 | 0 | 0 | 3 | 4 | 5 | 7 | 8 | 9 | 9 |
4 | 0 | 0 | 3 | 4 | 5 | 7 | 8 | 9 | 10 |
(1) 如,i=1,j=1,w(1)=2,v(1)=3,有j
故V(1,2)=max{ V(1-1,2),V(1-1,2-w(1))+v(1) }=max{0,0+3}=3;
(3) 如此下去,填到最后一个,i=4,j=8,w(4)=5,v(4)=6,有j>w(4),
故V(4,8)=max{ V(4-1,8),V(4-1,8-w(4))+v(4) }=max{9,4+6}=10;
void Find_Max()//动态规划
{
int i,j;//i为物品,j为容量
for(i=1;i<=number;i++)
{
for(j=1;j<=capacity;j++)
{
if(j<w[i])//包装不进
{
V[i][j]=V[i-1][j];
}
else//能装
{
if(V[i-1][j]>V[i-1][j-w[i]]+v[i])//不装价值大
{
V[i][j]=V[i-1][j];
}
else//前i-1个物品的最优解与第i个物品的价值之和更大
{
V[i][j]=V[i-1][j-w[i]]+v[i];
}
}
}
}
}
这是对01背包问题的重新理解,之前理解的不是很到位,总感觉少点什么。
这是最基本的背包问题,特点:每种物品只有一件,可以选择放还是不放。
用子问题定义状态:即F[i,v]表示前i件物品放入一个容量为v的背包可以获得的最大价值。
F[i,v]=max (F[i-1,v],F[i-1,v-ci]+Wi)
这个方程非常重要,基本上所有背包问题相关的问题都是由它衍生出来的
这里有必要详细解释一下
将前i件物品放入容量为v的背包中,这个子问题,若只考虑第i件物品的策略(放还是不放),
那么可以转换为一个只和前i-1件物品相关的问题。如果不放第i件物品,那么问题就转换为
“前i-1件物品放入容量为v的背包中”,价值为F[i-1,v];如果放入第i件物品,那么问题就
转换为“前i-1件物品放入剩下的容量为v-ci的背包中”,此时能获得最大价值就是F[i-1,v-ci]
在加上第i件的物品的获得的价值Wi。
我们可知时间复杂度为O(VN),这不可改变,那么空间复杂度呢,上面代码用的二维数组所以空间复杂度O(VN),
那么我们是否可以优化空间复杂度,如果可以只能是一维数组了。
如何只用一个数组?看上面代码是从容量为0开始遍历,那反向遍历这样就可以用以为数组代替。
核心思想:
for i:=1 to N do
for j=V downto c[i] do
if f[j-c[i]]+w[i]>f[j] then
f[j]=f[j-c[i]]+w[i];
void Find_max2()
{
int i,j;//i为物品,j为容量
for(i=1;i<=number;++i)//number为个数
{
for(j=1;j<=capacity;++j)//capacity为背包体积
{
if(v[j-c[i]]+w[i]>v[j])
{
v[j]=v[j-c[i]]+w[i];
}
}
}
}
在最优解的问题中,事实上有两种不太相同的问法。
1.恰好装满背包时的最优解
初始化时将f[0]为0,其余f[1…N]设为负无穷,这样可以保证最终解是恰好装满背包的。
2.只求价值最大
初始化将f[0…N]都设为0
原因:初始化数组F数组事实上就是在没有任何物品可以放入背包时的合法状态。如果要求背包恰好装满,那么此时只有容量为0的背包可能被价值为0的nothing“恰好装满”,其它容量的背包均没有合法的解,属于未定义的状态,它们的值就都应该是-∞了。如果背包并非必须被装满,那么任何容量的背包都有一个合法解“什么都不装”,这个解的价值为0,所以初始时状态的值也就全部为0了。