完全背包问题跟01背包的区别是01背包每个物品只能选一次,总共就这几个,而完全背包问题是每个物品可以无限选,只要装得下。可以看成是有几种物品,每种都无限多个。
如何根据01背包问题的分析成果来分析完全背包呢?其实很简单,
.01背包在选第i个物品时,容积够用情况下,只有2种状态可选,放还是不放,找出最大价值的选择
而完全背包在选第i种物品时,容积够用情况下,可能有2种以上状态可选,放1个,或者2个,3个,或者不放。找出最大价值的选择
可以利用k = j/weight[i]算出最多可以放几个,然后状态转移方程改为 V[i][j] = max(V[i – 1][j – k*weight[m]] + k * value[i]) 从0到k遍历一遍求出最大值即可
代码如下:
void packageTotal()
{
for(int i = 1; i <= N; i++)
{
for(int j = 1; j <= M; j++)
{
if(weight[i] > j) //如果重量比容量大,放不下,就不放
{
V[i][j] = V[i - 1][j];
}
else //其实 if跟else可以去掉的
{
int k = j / weight[i]; //如果能放下,要看看能放几个,然后放几个跟不放所有情况比一下看哪个价值最大,除法默认向下取,也可以用floor()
for(int m = 0; m <= k; m++)
{
if(V[i - 1][j - m * weight[i]] + m * value[i] > V[i][j])
{
V[i][j] = V[i - 1][j - m * weight[i]] + m * value[i];
}
}
}
}
}
}
其实if else判断是多余的,通过k的大小就能判断了,更改代码如下:
void packageTotal()
{
for(int i = 1; i <= N; i++)
{
for(int j = 1; j <= M; j++)
{
int k = j / weight[i]; //如果能放下,要看看能放几个,然后放几个跟不放所有情况比一下看哪个价值最大
for(int m = 0; m <= k; m++)
{
if(V[i - 1][j - m * weight[i]] + m * value[i] > V[i][j])
{
V[i][j] = V[i - 1][j - m * weight[i]] + m * value[i];
}
}
}
}
}
优化:其实这个跟01背包问题是很像的,每次写入数据都是根据上一行的结果写入的,数组一维就够用了,可以像更改01背包二维至一维那样去改,参考我的01背包问题的博文,
更改如下:
void packageTotal()
{
for(int i = 1; i <= N; i++)
{
for(int j = M; j >= 1; j--) //跟01背包一样,也是从右向左更新数据,因为更新时候需要上次循环左边的数据
{
int k = j / weight[i]; //如果能放下,要看看能放几个,然后放几个跟不放所有情况比一下看哪个价值最大
for (int m = 0; m <= k; m++)
{
if (V1[j - m * weight[i]] + m * value[i] > V1[j])
{
V1[j] = V1[j - m * weight[i]] + m * value[i];
}
}
}
}
}
这样就完美了吗?不,还可以继续优化,感觉有时候代码就像艺术品,可以简洁到令人难以置信,它的更改跟01背包的最终优化代码惊人的相似,只是从写入数据时从前往后写了,其它全部不变。
代码如下:
void packageTotal()
{
for(int i = 1; i <= N; i++ )
{
for(int j = 1; j <= M; j++) //相比01背包最终优化代码,只是换了遍历方向而已
{
if(j >= weight[i])
{
V1[j] = max(V1[j], V1[j - weight[i]] + value[i]);
}
}
}
}
为什么只是换了遍历方向就可以由01背包变成了完全背包呢,这样不是就利用了当前这一次更新数组的左边的数据了吗,上一次01背包为了避免这种情况发生专门从右向左写数据,而这个刚好相反,我这次写数据从左往右写,前面写了以后,留给后面的作参考,因为随着一位数组横向写入数据越多,j也在变大,j变大,就可以看看有没有东西可以继续塞进去,因为01背包一个物品放进去后就不能再用了,而完全背包还可以利用,所以当前这一次对某个物品做出选择后还可以继续选择,也就是说还可以再放第2个该物品,第三个,等等,所以完全背包每次做出选择是取决于当前这一步的,而01背包每次做出选择是取决于上一步的。主要就是因为当前这一步一个物品放过以后,表格逐渐向右填写,随着可放空间的增加,可以判断这一步是否还可以再放一个当前的物品。跟前面那个求出当前可放的最大物品数,然后从0个放到最大个,本质是一样的。