• 92 背包问题


    原题网址:https://www.lintcode.com/problem/backpack/description

    描述

    在n个物品中挑选若干物品装入背包,最多能装多满?假设背包的大小为m,每个物品的大小为A[i]

    你不可以将物品进行切割。

    您在真实的面试中是否遇到过这个题?  

    样例

    如果有4个物品[2, 3, 5, 7]

    如果背包的大小为11,可以选择[2, 3, 5]装入背包,最多可以装满10的空间。

    如果背包的大小为12,可以选择[2, 3, 7]装入背包,最多可以装满12的空间。

    函数需要返回最多能装满的空间大小。

    挑战

    O(n x m) time and O(m) memory.

    O(n x m) memory is also acceptable if you do not know how to optimize memory.

    标签
    背包问题
    LintCode 版权所有
    动态规划(DP)

     

    思路:

    背包问题是动态规划的一种题型,它的特点如下:

    1. 用值作为dp维度
    2. dp过程就是填写矩阵
    3. 可以用滚动数组进行优化  转自此文

    dp【i】【j】表示前 i 个物品放到容量为 j 的背包里能够占用的最大体积。

    状态转移方程为:dp【i】【j】= max(dp【i-1】【j】,dp【i-1】【j-A【i】】+A【i】)。

    每个物品只有两种状态,放或者不放。对于容量为 j 的背包,放入第 i-1 件物品后占用的最大体积为dp【i-1】【j】,现在考虑第 i 件物品。

    不放,dp【i】【j】=dp【i-1】【j】;

    放,需要从 j 中腾出A【i】的空间,再看剩余的空间放前 i-1 件物品最大能占多少空间,即dp【i】【j】=A【i】+ dp【i-1】【j-A【i】】。(注意前提是 j >= A【i】)

    最后的dp【i】【j】就是上述两种情况的较大值。

     

    AC代码,时间复杂度O(m×n),空间复杂度O(m×n):

    class Solution {
    public:
        /**
         * @param m: An integer m denotes the size of a backpack
         * @param A: Given n items with size A[i]
         * @return: The maximum size
         */
        int backPack(int m, vector<int> &A) {
            // write your code here
        if (A.empty())
        {
            return 0;
        }
        int size=A.size();
        vector<vector<int>> dp(size,vector<int>(m+1,0));
        //初始化第一行,即只有第一个物品时,若背包容量大于等于A[0],放物品能占用的最大空间为A[0];
        for (int j=0;j<=m;j++)
        {
            if (j>=A[0])
            {
                dp[0][j]=A[0];
            }
        }
        //计算dp其他元素;
        for (int i=1;i<size;i++)
        {
            for (int j=0;j<=m;j++)
            {
                if (j>=A[i])//能放A[i],计算此时的最大体积,注意是大于等于;
                {
                    dp[i][j]=dp[i-1][j-A[i]]+A[i];
                }
                dp[i][j]=max(dp[i-1][j],dp[i][j]);
            }
        }
        return dp[size-1][m];
        }
    };

     

    PS:状态转移方程简而言之就是,背包容量为 j 时,能放得下 i 就腾出A【i】的空间,再看剩余的空间放前 i-1 件物品最大能占多少空间,二者之和与不放 i 能占用的最大体积比,哪个大就取哪个;放不下就直接看0~i-1能占用 j 的最大容量是多少;

    再PS:dp数组优化前,j是从前向后遍历还是从后向前遍历都不影响其结果,因为计算当前i是参照i-1时的数据。 

     

    利用滚动数组优化空间复杂度:

    创建一维动态数组dp,dp【j】仍然表示前 i 个物品放到容量为 j 的背包里能够占用的最大体积。

    状态转移方程为:j从m依次递减到0,dp【j】= max(dp【j】,dp【j-A【i】】+A【i】)。

    推导思路与二维dp相同。只不过代码实现的时候要注意,列方向需要从后向前计算(防止覆盖未参加计算的上一行数据),这样就实现了用当前行不断代替前一行。

    不难理解,i=0时是正常计算的。i≠0时,二维dp【i】【j】= max(dp【i-1】【j】,dp【i-1】【j-A【i】】+A【i】),注意等号右边的dp数组行下标都是i-1,即前一行的数据。一维dp时,j从后向前遍历,则参与计算的dp【j】与dp【j-A【i】】都是上一行的值。

     

    AC代码:

    class Solution {
    public:
        /**
         * @param m: An integer m denotes the size of a backpack
         * @param A: Given n items with size A[i]
         * @return: The maximum size
         */
        int backPack(int m, vector<int> &A) {
            // write your code here
        int size=A.size();
        vector<int> dp(m+1,0);
        for (int i=0;i<size;i++)
        {
            for (int j=m;j>=0;j--)//从后向前计算,保证了参与运算的是上一行的dp[j]与dp[j-A[i]];
            {
                if (j>=A[i])//注意是大于等于;
                {
                    dp[j]=max(dp[j],dp[j-A[i]]+A[i]);
                }
            }
        }
        return dp[m];
        }
    };

     

     

    参考:

    lintcode backpack 背包问题  讲解清晰易懂

    lintcode:背包问题

    【LintCode】Backpack 背包问题

    LintCode背包问题总结  总结了一系列背包问题,可以好好参考。

    [LintCode] Backpack I II III IV V VI [背包六问]

     

     

  • 相关阅读:
    Kentico的UIPager的pagesize不工作
    哈啰单车流量问题
    安卓手机无限重启是怎么回事?
    Kentico的翻译功能
    Remote Desktop Free Manager
    访问存储在服务器中的CDR
    保持观察者状态达到跳出或不迷
    高通IPQ4019
    satixnet satellite modem T1000e
    802.11n 中HT20 HT40的区别和信道划分及plus,minus含义
  • 原文地址:https://www.cnblogs.com/Tang-tangt/p/9374818.html
Copyright © 2020-2023  润新知