动态规划(2)——0-1背包问题

一、问题描述

        有n件物品,还有一个最多装重量为w的背包,第i件物品重量是w[i],其价值为v[i],每件物品只能装一次,那么怎样装物品,才能使装入背包物品中的价值总和最大。

        其实这道题在各大题目网站上,应该都不会出现原题,但许多动态规划类题目的思路其实来源于此题,所以关键是掌握基础的,再将题目转化为01背包问题。

二、算法思路

        其实这道题的最简单想法,那就是利用暴力回溯,枚举出所有的背包装物品的情况,但这显然于算法而言是一个非常费时间的解法,它的时间复杂度是指数级的,所以更需要使用动态规划。

        那么我们依然用上一章节的动态规划做题四步法(不太清楚的友友们可以去看博主的上一篇博客)

 1.明确dp数组以及其含义

        这里我们的目的是为了得到背包装的最大值,那么这里dp[i][j]的含义为在[0-i]之间的物品选择,放进背包容量为[j],价值的最大数是多少?可能了解这道题的同学会觉得很绕,那这里放一张图方便大家的理解。 

 2.推导递推公式

         其实递推公式的类型基本上就两种一种要么是求和(斐波那契),另一种就是这种求最大值。

         在这里,我们要时刻牢记dp数组的含义,由此数组可以得出推导的大概方向,

         1.不放这个物品——也就是0,那么此时dp[i][j]的大小就是上一个物品价值的最大值即dp[i][j] = dp[i-1][j],因为我没放东西,所以跟上一个相同。

        2.放下这个物品——也就是1,那么此时dp[i][j]就需减去i物品的重量,并且加上物品的价值,dp[i][j] = dp[i-1][j-w[i]] + v[i].

        那么递推公式就为 dp[i][j] = max(dp[i-1][j] , dp[i-i][j-w[i]]+v[i]).

 3.初始化

        关于动态规划的初始化,通俗的来讲,是将最底层的子问题给初始化掉,就像斐波那契数一样,需要求得dp[0]和dp[1]来求解,但其实也要根据dp数组含义来进行初始化。

        所以我们回顾上面那张图,要将第一列和第一行给初始化掉,即dp[0][j]和dp[i][0],因为第一列,背包可重量为0,所以背包所能获得的价值也为0。

然后我们再看dp[0][j]随着可容重量的增加,接下来的价值就为v[0],即当j>=w[0]时,d[0][j]就为v[i]。

初始化情况如图:

4遍历顺序的确定

        其实从图片来看,这道题可以从两个方向来遍历,物品和背包。那应该先遍历物品还是先遍历背包呢?其实两者都可以!

       但个人觉得先遍历物品再去遍历背包会方便理解些,但发生这种情况是为什么,那就是因为递归最根本的性质问题。我们的递归公式 dp[i][j] = max(dp[i-1][j] , dp[i-i][j-w[i]]+v[i]). 可以看到是靠里面两个选来得出的。

三、代码

int main( )
{
    vector<int> w = {1,4,6};
    vector<int> v = {10,20,30};
    int bag_weight = 6;

    vector<vector<int>> dp(w.size(),vector<int>(bag_weight + 1,0));
        
    for(int j = w[0];j <= bag_weight;j++)
        dp[0][j] = v[0];
    

    for(int I=0;i<w.size;i++)
        for(int j = 0;j<=bag_weight;j++)
        if(j<w[I])
            dp[i][j] = dp[i-1][j];
        else
            dp[i][j] = max(dp[i-1][j],dp[i-1][j-w[i]] + v[i];
       

      cout << dp[w.size-1][bag_weight];

    return 0;
}    

  

四、总结

        做动态规划类的题目,其实可以多在纸上推导递推过程,这样既能提高效率,又能提高准确率。做法题很切忌纸上谈兵,瞎想,到头来啥都想不出,自己也要多在程序里测试dp数组的正确性。

  • 20
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值