【动态规划 背包问题】

416. 分割等和子集(背包)

class Solution {
public:
    // dp[i][j] 能获得的最大价值:
    //      i --> 代表前i件商品
    //      j --> 能得到的重量j
    // 0/1背包状态转移方程:
    //      dp[i][j] = max(dp[i-1][j-w[i]] + v[i], dp[i-1][j]);
    bool canPartition(vector<int>& nums) {
        int sum = [](vector<int> &n){
            int num = 0;
            for(auto i: n)
                num += i;
            return num;
        }(nums);
        if(sum % 2)
            return false;
        int target = sum / 2;
        int len = nums.size();
        int dp[len][target+1];
        for(int j=0;j<=target;++j){
            if(j >= nums[0])
                dp[0][j] = nums[0];
            else
                dp[0][j] = 0;
            if(dp[0][j] == target)
                return true;
        }
        for(int i=1;i<len;++i){
            int v = nums[i];
            int w = nums[i];
            dp[i][0] = 0;
            for(int j=1;j<=target;++j){
                if(j < w)
                    dp[i][j] = dp[i-1][j];
                else
                    dp[i][j] = max(dp[i-1][j-w] + v, dp[i-1][j]);
                if(dp[i][j] == target)
                    return true;
            }
        }
        return false;
    }
};

进行空间优化

class Solution {
public:
    // dp[i][j] 能获得的最大价值:
    //      i --> 代表前i件商品
    //      j --> 能得到的重量j
    // 0/1背包状态转移方程:
    //      dp[i][j] = max(dp[i-1][j-w[i]] + v[i], dp[i-1][j]);
    bool canPartition(vector<int>& nums) {
        int sum = [](vector<int> &n){
            int num = 0;
            for(auto i: n)
                num += i;
            return num;
        }(nums);
        if(sum % 2)
            return false;
        int target = sum / 2;
        int len = nums.size();
        int dp[2][target+1];
        for(int j=0;j<=target;++j){
            if(j >= nums[0])
                dp[0][j] = nums[0];
            else
                dp[0][j] = 0;
            if(dp[0][j] == target)
                return true;
        }
        for(int i=1;i<len;++i){
            int cur = i % 2;
            int last = (i + 1) % 2;
            int v = nums[i];
            int w = nums[i];
            dp[cur][0] = 0;
            for(int j=1;j<=target;++j){
                if(j < w)
                    dp[cur][j] = dp[last][j];
                else
                    dp[cur][j] = max(dp[last][j-w] + v, dp[last][j]);
                if(dp[cur][j] == target)
                    return true;
            }
        }
        return false;
    }
};

474. 一和零(多维费用背包)

class Solution {
public:
    int findMaxForm(vector<string>& strs, int m, int n) {
        vector<pair<int, int> > nums;
        for_each(begin(strs), end(strs), [&nums](auto str){
            int i = 0, j = 0;
            for(char ch : str)
                if(ch == '0') i++;
                else j++;
            nums.push_back(make_pair(i, j));
        });
        int dp[m+1][n+1];
        memset(dp , 0 , sizeof(dp));
        int result = 1;
        dp[0][0] = 1; 
        for(auto num : nums){
            int v = num.first;
            int w = num.second;
            for(int i=m; i>=v; --i)
                for(int j=n; j>=w; --j){
                    dp[i][j] = max(dp[i][j], (dp[i-v][j-w] ? dp[i-v][j-w] + 1 : 0));
                    // cout << dp[i][j] << " "  << i << " " << j << endl;
                    result = max(dp[i][j], result);
                }
        }
        return result - 1;
    }
};

322. 零钱兑换(完全背包)

硬币可以重复使用。
完全背包问题。完全背包只需要将 0-1 背包的逆序遍历 dp 数组改为正序遍历即可。

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        int dp[amount + 1];
        int len = coins.size();
        memset(dp, 0 , sizeof(dp));
        dp[0] = 1;
        for(auto coin: coins)
            for(int i = coin; i<=amount; ++i)
                dp[i] = dp[i-coin] ?  (dp[i] ? min(dp[i-coin] + 1, dp[i]): dp[i-coin] + 1) : dp[i];
                // cout << dp[i] << " " << i << " " << coin << endl;
        return dp[amount] - 1;
    }
};

518. 零钱兑换 II(完全背包)

请你计算并返回可以凑成总金额的硬币组合数

class Solution {
public:
    int change(int amount, vector<int>& coins) {
        int dp[amount+1];
        memset(dp, 0 ,sizeof(dp));
        dp[0] = 1;
        for(auto coin : coins){
            for(int i=coin; i<=amount; ++i){
                dp[i] +=  dp[i-coin];
                // cout << dp[i] << " " << i << " " << coin << endl;
            }
        }
        return dp[amount];
    }
};

139. 单词拆分(完全背包)

class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        int s_len = s.size();
        int len, w_len = wordDict.size();
        int dp[s_len + 1];
        memset(dp, 0 , sizeof(dp));
        dp[0] = 1;
        for(int i=0;i<s_len;++i){
            for(auto &word : wordDict){
                len = word.size();
                if(i + 1 < len)
                    continue;
                if(word == s.substr(i+1-len, len))
                    dp[i+1] |= dp[i+1-len];
            }
        }
        return dp[s_len];
    }
};

377. 组合总和 Ⅳ(完全背包)

class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        int dp[target+1];
        sort(nums.begin(), nums.end());
        memset(dp, 0 , sizeof(dp));
        dp[0] = 1;
        for(int i=1;i<=target;++i)
            for(int num : nums){
                if(i < num || dp[i] >= INT_MAX - dp[i-num])   // 要注意一些奇奇怪怪的边界条件!!
                    continue;
                dp[i] += dp[i-num];
            }
        return dp[target];
    }
};
posted @ 2022-03-02 18:06  fwx  阅读(21)  评论(0)    收藏  举报