完全背包

题目链接:52. 携带研究材料(第七期模拟笔试) (kamacoder.com)

思路:完全·背包问题和01背包的区别在于同一个物品可以被重复放入,在代码里的区别就是内部遍历背包的for循环由倒序变成了正序。而且如果我们压缩了一维的话,如我的做法,两个for循环的顺序也是无所谓的。

#include<iostream>
#include<vector>
using namespace std;
int main(){
    int n,v;
    cin>>n>>v;
    vector<int> dp(v+1,0);
    vector<vector<int>> bag(n+1,vector<int>(2,0));
    for(int i=0;i<n;i++){
        cin>>bag[i][0]>>bag[i][1];
    }
    for(int j=0;j<n;j++){
        for(int k=1;k<=v;k++){
            if(k>=bag[j][0])
                dp[k]=max(dp[k],dp[k-bag[j][0]]+bag[j][1]);
        }
    }
    
    cout<<dp.back();
    return 0;
}

零钱兑换 II 

题目链接:518. 零钱兑换 II – 力扣(LeetCode)

思路:记住状态转移方程不是dp[j]=dp[j-coins[i]]+1而是dp[j]+=dp[j-coins[i]];

不过值得注意的是:

在求装满背包有几种方案的时候,认清遍历顺序是非常关键的。

如果求组合数就是外层for循环遍历物品,内层for遍历背包

如果求排列数就是外层for遍历背包,内层for循环遍历物品

class Solution {
public:
    int change(int amount, vector<int>& coins) {
        vector<int> dp(amount+1,0);
        dp[0]=1;
        for(int i=0;i<coins.size();i++){
            for(int j=1;j<=amount;j++){
                if(j>=coins[i])dp[j]+=dp[j-coins[i]];
            }
        }
        return dp.back();
    }
};

组合总和 Ⅳ  

题目链接:377. 组合总和 Ⅳ – 力扣(LeetCode)

思路:用long都能超,我真是服了这个样例了。

避免超限制的方法:加一个判断dp[j] < INT_MAX - dp[j - nums[i]]因为这一题动态规划有个坑,就是答案保证最终答案的组合数在32位范围内,但是如果在taraget之前的数字组合数是可能超过INT_MAX的,甚至更大,但因为最终答案不会超过,所以target肯定不会利用到这些超过INT_MAX的数据的,所以忽略掉那些会超过INT_MAX的就可以。

但是莫名其妙的改成unsigned int 就能通过样例,long就不行。

值得注意的是尽管本题问的是组合数,但是事实上答案是排列数目,因此如上一题所说,我们要先遍历背包容量,再遍历物品。

class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        vector<int> dp(target + 1, 0);
        dp[0] = 1;
        for (int j = 1; j <= target; j++) {
            for (int i = 0; i < nums.size(); i++) {
                if (j >= nums[i] && dp[j] < INT_MAX - dp[j - nums[i]])
                    dp[j] += dp[j - nums[i]];
            }
        }
        return dp.back();
    }
};

 

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。