动态规划之01背包问题入门

版权声明:装作自己有版权 https://blog.csdn.net/weixin_44574444/article/details/86650964

01背包问题

转载一篇很好的01背包问题的博客

01背包问题描述:有编号分别为a,b,c,d,e的五件物品,它们占据的空间量分别是2,2,6,5,4,它们的价值分别是6,3,5,4,6,每件物品数量只有一个,现在给你个空间量为10的背包,如何让背包里装入的物品具有最大的价值总和?

我们先用递推的方式求解f[i][j]表示做出i号选择时还有j的空间量,我们按照动态规划的思想(i号物品放还是不放):

1:

当i号物品要放入时,则f[i][v]=f[i-1][v-w[i]]+p[i],表示(p[i]表示i号物品的价值),前i-1次选择后所选物品放入容量为v-w[i]的背包所获得最大价值为f[i-1][v-w[i]],加上当前所选的第i个物品的价值p[i]即为f[i][v]。

2

当i号物品不放入时吗,则f[i][j]=f[i-1][j]。则表示为前i-1次选择所得的最大的价值。

所以,我们可以得出01背包的递推公式:f[i][v] = max{f[i-1][v], f[i-1][v-w[i]]+p[i]}

有了这个递推式,我们可以打出一下模版

for(int i = 1; i <= n; i++)
        {
            for(j = w[i]; j <= V; j++)
                f[i][j] = max(f[i-1][j], f[i-1][j-v[i]]+w[i]);
        }

如果我们用f[i][j]来存数的话,我们不需要的数也将会存进,那么就浪费了很多空间,因此,我们可以将二维转化为一维数组。

为什么呢,我们观察,f[i][v] = max{f[i-1][v], f[i-1][v-w[i]]+p[i]},这个式子中,f[i][j]只跟i-1次选择有关,也就是说,我们每一次只需要列举出它的上一次选择就可以推出f[i][j]的状态,因此我们只需要用一维数组就可以维护出这个状态。
即第i次求f[v]时,我们只需要知道f[v]和f[v-w[i]]表示的是不是f[i-1][v]和f[i-1][v-w[j]],事实上我们只需要将内层循环变为从V到w[j]的逆向循环即可满足要求。

为什么正向循环为何不可,因为此时f[v]保存的相当于是f[i][v]和f[i][v-w[i]]

在这里插入图片描述
在滚动的时候,只有如图所示的两格与黑色的那一格有关如果我们从小到大来循环,当我们开始给黑色的那一格附值的时候,第一个绿色的格子的值已经不再是它本来的值了,已经被橙色的那一格给盖住了(一维数组可以当作一行来看待,第一个绿色格子的值会当成橙色格子的值,不懂的可以自己画一画图)

具体代码实现如下

for(int i=1;i<=n;i++)//有n个物品
    {
        for(int j=h;j>=c[i];j--)//从大到小枚举,一定要大于物品本身的价值,否则j-c[i]就为负数,那就等着wa吧
        {
            f[j]=max(f[j],f[j-c[i]]+w[i]);//每次取最大
        }
    }

猜你喜欢

转载自blog.csdn.net/weixin_44574444/article/details/86650964