【Leetcode动态规划--完全背包】279.完全平方数 139.单词拆分 322.零钱兑换 518.零钱兑换 II(求装满背包的方法数) 377.组合总和 Ⅳ


完全背包详解

Leetcode279.完全平方数

1.问题描述

在这里插入图片描述

2.解决方案

详细过程在代码sxl中,这一个典型的完全背包并且求的是最小个数
1.求最少个数,那就意味这组合还是排列都一样,所以遍历顺序先后物品背包都可以
2.初始化dp[0]=0,其他用最大值,这个前面也提到很多次了
3.第二层 j 应该从item开始遍历,这个多注意

错误:dp[j] 的定义中 j 时背包容量,所以无论遍历顺序如何 dp[j] 那肯定找准背包,所以一开始没整明白,搞了个dp[i] i 时物品那肯定不对了

//dp[i]=min(dp[i],dp[i-item]+1);
dp[j]=min(dp[j],dp[j-item]+1);
class Solution {
public:
    int numSquares(int n) {
        //1.

        //2.
        vector<int> dp(n+1,INT_MAX);
        dp[0]=0;

        //3.
        for(int i=1;i*i<=n;i++){ //先物品
            int item=i*i;
            for(int j=item;j<=n;j++){ //后正序背包
                //dp[i]=min(dp[i],dp[i-item]+1);
                dp[j]=min(dp[j],dp[j-item]+1);
            }
        }

        //4.
        return dp[n];
    }
};




Leetcode139.单词拆分

1.问题描述

在这里插入图片描述

2.解决方案

想法一:自己的想法

代码sxl上有我的思路,正确的递推式子但是由于字符串操作不熟悉没实现
dp[i] = dp[i] || dp[i-遍历到的物品字符串]
即dp[i]是true当且仅当,取当前物品字符串为真或者不取当前字符串为真为真,有一个为真即可

//自己的想法(想法没毛病,就是字符串操作不熟悉和没想明白)
class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        //1.
        int len=s.size();

        //2.
        vector<bool> dp(len+1, false);

        //3.
        for (auto item : wordDict) {
            for(int i=item.size();i<=len;i++){
                //取到s[0]到s[i]闭区间
                string bag(s.begin(),s.begin()+i+1);
                vector<string> Bag(s.begin(),s.begin()+i+1);
                int index=bag.find(item);
                if(index!=string::npos){
                }
            }
        }

        //4.
    }
};

想法二:正确的递推方式

代码sxl有详细解答
1.递推方式太优秀了,dp[i] = [j,i]这个字串出现在物品中 && dp[j]==true
2.初始化方式老生常谈
3.遍历方式很有说法,因为题目特殊性,需要验证是否为背包的子集,所以要先背包

//代码sxl完全背包
class Solution{
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        //1.为了好查找,因为vector不可以查找
        int len=s.size();
        unordered_set<string> goods(wordDict.begin(),wordDict.end());

        //2.
        vector<bool> dp(len+1, false);
        dp[0]=true;

        //3.由于题目特殊性要看看wordDict里面是不是s的子集,所以选择把s放在外层也就是先遍历背包
        for(int i=0;i<=len;i++){
            for(int j=0;j<=i;j++){
                string word=s.substr(j,i-j);
                if(goods.find(word)!=goods.end()&&dp[j]==true) dp[i]=true;
            }
        }

        //4.
        return dp[len];


    }
};




Leetcode322.零钱兑换

1.问题描述

在这里插入图片描述

2.解决方案

解法一:自己的dp(仿造完全背包问题)

1.dp数组没问题
2.递归没问题
3.dp数组初始化,要用最大值,因为求的是min(),如果不用最大容易无法更新到这个值

vector<int> dp(amount+1, INT_MAX);
dp[0]=0;

4.遍历问题(这个代码随想录中有总结)
5.如果是初始值,可以直接跳

if(dp[i-item]==INT_MAX) continue;
class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        //1.
        int len=coins.size();

        //2.
        vector<int> dp(amount+1, INT_MAX);
        dp[0]=0;
        for (auto item: coins) {
            for(int i=item;i<=amount;i++){
                if(dp[i-item]==INT_MAX) continue;
                dp[i]=min(dp[i],dp[i-item]+1);
            }
        }

        //3.
        if(dp[amount]==INT_MAX) return -1;
        return dp[amount];

    }
};


解法二:官方的dp(和解法一自己的dp代码相似,但是思路大不相同,没看)

在这里插入图片描述

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




Leetcode518.零钱兑换 II(求装满背包的方法数)

1.问题描述

在这里插入图片描述

2.解决方案

1.主要是把此问题转化成完全背包问题

2.遍历顺序问题,先物品后背包得到组合数(无顺序),先背包后物品得到排列数(有顺序)

3.递推是取不取相加,对应边界dp[0]=1,通过含义也可以得到或者由于是递推的终点所以必须是1

4.其他值初始化,由于是求相加为了不影响肯定是0了


class Solution {
public:
    int change(int amount, vector<int>& coins) {
        //1.
        //int len=coins.size();

        //2.
        vector<int> dp(amount+1,0);
        dp[0]=1;

        //3.
        for (auto item : coins) {
            for(int i=item;i<=amount;i++){
                dp[i]=dp[i]+dp[i-item];
            }
        }

        //4.
        return dp[amount];
    }
};



Leetcode377.组合总和 Ⅳ

1.问题描述

在这里插入图片描述

2.解决方案

1.这个题除了遍历顺序和上一题518一摸一样,518求的是组合数,这一题求的是排列数,只需要将遍历顺序变为先背包后物品

2.一个需要注意的点,就是只有当 i>=item 背包值大于物品值时才有取和不取两种选择,为dp[i]=dp[i]+dp[i-item] ,如果是 i<item 即 背包值小于物品值,只有不取这一种选择,即恒为 dp[i]=dp[i],当然这一部分可以不变可以直接不写,但是要清楚为什么

if(i>=item&&dp[i]<(INT32_MAX-dp[i-item])) dp[i]=dp[i]+dp[i-item];
if(i<item) dp[i]=dp[i];

3.数据溢出的问题,两个数都是int相加大于了int会报溢出的错误,所以要加上溢出判断,以下为两个版本的判断,很明显第一种 (dp[i]+dp[i-item])<INT32_MAX 不对因为虽然是判读了,但是相加操作以及进行了已经溢出了

//if(i>=item&&(dp[i]+dp[i-item])<INT32_MAX) dp[i]=dp[i]+dp[i-item];
if(i>=item&&dp[i]<(INT32_MAX-dp[i-item])) dp[i]=dp[i]+dp[i-item];


class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        //1.
        //int len=nums.size();

        //2.
        vector<int> dp(target+1,0);
        dp[0]=1;

        //3.
        for(int i=0;i<=target;i++){
            for (auto item : nums) {
                //if(i>=item&&(dp[i]+dp[i-item])<INT32_MAX) dp[i]=dp[i]+dp[i-item];
                if(i>=item&&dp[i]<(INT32_MAX-dp[i-item])) dp[i]=dp[i]+dp[i-item];
                //if(i<item) dp[i]=dp[i];
            }
        }

        //4.
        return dp[target];
    }
};
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值