【代码随想录】刷题Day45

文章介绍了使用动态规划解决一系列问题,包括将爬楼梯问题抽象为完全背包问题,通过遍历背包和物件找到所有可能的排列;零钱兑换问题中寻找最小硬币数量,利用dp数组更新最小值;完全平方数问题,寻找最小数量的完全平方数之和;以及单词拆分问题,通过dp数组判断字符串是否可由字典中的单词组成。
摘要由CSDN通过智能技术生成

1.完全背包解决爬楼梯问题

70. 爬楼梯

1.爬楼梯抽象成背包问题就是:物品有1和2,而背包容量有n个,算出不同的排列结果。因为走楼梯能在1格和2格之间任意选,这样是完全背包的理由;并且每一次走楼梯是有顺序的,因此它涉及的问题是排列问题。

2.那么我们知道是完全背包的排列问题,只需要先遍历背包再遍历物件就能求出所有的结果,该题的思路依然是:走到j位置必须是走到j-1位置和走到j-2位置方法的和

class Solution {
public:
    int climbStairs(int n) {
        vector<int> dp(n+1,0);
        dp[0]=1;
        for(int j=0;j<=n;j++)
        {
            for(int i=1;i<=2;i++)
            {
                if(j>=i)
                    dp[j]+=dp[j-i];
            }
        }
        return dp[n];
    }
};

2.零钱兑换

322. 零钱兑换

1.dp数组的含义:dp[j]指j位置下,最小数量能符合背包容量的个数。

2.dp数组的条件:针对i物件时,dp[j]=dp[j-coins[i]]+1,那么既然需要找的是最少数量的方法,那么条件就dp[j]=min(dp[j],dp[j-coins[i]]+1);

3.初始化:由条件知道dp[0]=0,此外由于我们求的是最小值,那么数组除0外的其他位置都不能初始化为0;因为如果是0,dp[j]=min(dp[j],dp[j-coins[i]]+1),不管怎么更新初始化的值都会影响条件的生成,那么我们初始化其他的位置为amount+1,这样就不会干扰到其他位置的条件了。

4.本题考虑的是结果的个数。之前讲过的完全背包有组合和排列的问题其实就不需要考虑,因为不论组合还是排列,最少的个数都是一样的结果。不过组合能够优化一部分效率就是了,因为他们不需要考虑比排列多的那些情况。

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

3.完全平方数

279. 完全平方数

1.本题的dp数组含义与上一题基本一致:dp[j]指j位置下,最小数量能符合背包容量的个数

2.dp数组的条件:首先我们遍历的是i,但是条件中需要的是完全平方数,所以物件就是i*i,针对i物件时,dp[j]=dp[j-i*i]+1,那么既然需要找的是最少数量的方法,那么条件就dp[j]=min(dp[j],dp[j-i*i]+1)

3.当考虑dp[0]时,没有数能代表,所以dp[0]=0;此外由于我们求的是最小值,那么数组除0外的其他位置都不能初始化为0,所以由于1恰好也是完全平方数,那最保守的结果也全是1的结果,自然种类有n个。那么我们初始化其他位置的dp数组为n即可

4.遍历的写法问题:首先物件i的遍历,由于我们不能确定n到底是不是完全平方数,那么考虑范围的时候自然要把n带上,所以i<=n;遍历背包时,j的初始值应该为i*2,因为我们刚刚理解到其实i*i才是物件。

class Solution {
public:
    int numSquares(int n) {
        vector<int>dp(n+1,n);
        dp[0]=0;
        for(int i=0;i<=n;i++)
        {
            for(int j=i*i;j<=n;j++)
            {
                dp[j]=min(dp[j],dp[j-i*i]+1);
            }
        }
        return dp[n];
    }
};

day46

4.单词拆分

139. 单词拆分

1.dp数组的含义:dp[i]为i位置下,是否有满足条件的字符能组成0到i位置的字符串。如果有则是true。反之为false

2.首先,并不是只要找到符合背包的局部字符串能对上的字符串就算成功的,字符串的组合是有先后顺序的,那么考虑遍历顺序时应该使用先遍历背包再遍历物件的方法进行排列找结果

3.对于单个的字符串符合的条件:当前位置为j,之前的位置为i。由于是从前向后顺序判别的,假设i位置的值判断已经为true,说明i位置符合要求能找到对应上的值,那么我们只需要在要找的字符串[i,j]位置上判断是否有同样存在的字符串组合,那么我们就能得到dp[j]也是true

class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        unordered_set<string> wordSet(wordDict.begin(), wordDict.end());
        vector<bool>dp(s.size()+1,false);
        dp[0]=true;
        for(int j=0;j<=s.size();j++)
        {
            for(int i=0;i<j;i++)
            {
                string word = s.substr(i,j-i);
                if(wordSet.find(word)!=wordSet.end()&&dp[i])
                    dp[j]=true;
            }
        }
        return dp[s.size()];
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

灼榆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值