背包问题详解-这都不会可不行

背包问题详解-这都不会可不行

啰嗦几句

背包问题难度不高,就那么几个类,自己写一遍代码准没错。

0-1背包问题

对应leetcode:416、474、1049
动态规划解决这个问题。对某一物品,要么放入背包,要么不放,就这两种选择方式。对所有的物品都如此思考一遍后就得到了结论。用递归来实现动态规划肯定是一件不好的事情,慢,且容易栈溢出。
那就用循环呗,还有啥好商量的么?就是事前先定义好dp数组,根据状态数量确定是一维还是二维数组,然后搞就完事儿了。改成循环后,思考的方式就变了,变成:已经得知了第i个的最优解,如何求解第i+1的最优解。
这里如何判断是需要建立几维dp数组呢?很显然,是前多少人能达到多少数量,所以是二维。
另外压缩也了解一下吧,不然dp数组要爆炸了。
总的来说,这道题可以有三种写法,咱们就放最好的一种:压缩后的dp。上代码:
leetcode:416 medium 0-1背包问题

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        const int length = nums.size();
        int sum = 0;
        for (int i = 0; i < length; ++i)
            sum += nums[i];
        if (sum % 2 == 1)
            return false;

        const int halfSum = sum / 2;
        vector<bool> details(halfSum + 1, false);
        vector<bool> details_ori(halfSum + 1, false);
        for (int i = 0; i < length; ++i)
        {
            for (int j = 0; j < halfSum + 1; ++j)
            {
                if (i == 0)
                    details[j] = (nums[0] == j) ? true : false;
                else
                {
                    if(details_ori[j] == true)
                        details[j] = true;
                    else if(j == nums[i])
                        details[j] = true;
                    else
                        details[j] = (j > nums[i]) ? details_ori[j-nums[i]] : false;
                }
            }
            for (int k = 0; k < halfSum + 1; ++k)
                details_ori[k] = details[k];
        }
        return details[halfSum];
    }
};

我这缩减的还不够彻底其实可以改变一下顺序,将details_ori也取消掉。

完全背包问题

完全背包问题与0-1背包问题的不同之处在于,完全背包中物品的数量可以是任意的。
其实完全背包问题可以很轻易的转换成0-1背包问题。虽然物品的数量是无限的,但是由于背包会限制,所以其实还是个0-1背包问题。
上代码:
leetcode:518 medium 完全背包问题

class Solution {
public:
    int change(int amount, vector<int>& coins) {
        int coinsSize = coins.size();
        if(amount == 0)
            return 1;
        vector<int> details(amount+1, 0);
        for(int i = 0; i < coinsSize; ++ i)
        {
            details[0] = 1;
            for(int j = amount; j >= 1; -- j)
            {
                if(i == 0)
                    details[j] = (j%coins[0] == 0) ? 1 : 0;
                else
                {
                    int coinsNum = j / coins[i];
                    for(int p = 0; p < coinsNum; ++ p)
                        details[j] += details[j-(p+1)*coins[i]];
                }
            }
        }
        return details[amount];
    }
};

这下是把details_ori也删除掉了,内存消耗是非常小的,只是不知道为啥运行时间这么久。

混合背包

混合背包是在完全背包的基础上发展来的,每个物品的数量定一个常数(其实和完全背包没什么本质上的区别)

猜你喜欢

转载自blog.csdn.net/weixin_44039270/article/details/106379555