代码随想录2.18-2.19

动态规划

动态规划题目类型:

  1. 基础(包括斐波那契类)
  2. 背包
  3. 打家劫舍
  4. 股票
  5. 子序列

动规五部曲:
(1) dp数组以及下表的含义
(2)递推公式
(3)dp数组如何初始化
(4)遍历顺序:背包类尤其重要,两层for循环,先遍历背包再遍历物体
(5)打印dp数组:看看dp数组是否正确

509. 斐波那契数

70. 爬楼梯

分析之后发现就是斐波那契数的问题。这道题难点在于递推公式
拓展:如果一步可以走m个台阶,如何做

爬楼梯拓展

就是一步一个台阶,两个台阶,三个台阶,直到 m个台阶,有多少种方法爬到n阶楼顶。

一个绝佳的大厂面试题,第一道题就是单纯的爬楼梯,然后看候选人的代码实现,如果把dp[0]的定义成1了,就可以发难了,为什么dp[0]一定要初始化为1,此时可能候选人就要强行给dp[0]应该是1找各种理由。那这就是一个考察点了,对dp[i]的定义理解的不深入。

然后可以继续发难,如果一步一个台阶,两个台阶,三个台阶,直到 m个台阶,有多少种方法爬到n阶楼顶。这道题目leetcode上并没有原题,绝对是考察候选人算法能力的绝佳好题。

这一连套问下来,候选人算法能力如何,面试官心里就有数了。

其实大厂面试最喜欢的问题就是这种简单题,然后慢慢变化,在小细节上考察候选人。

class Solution {
public:
    int climbStairs(int n) {
        vector<int> dp(n + 1, 0);
        dp[0] = 1;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) { // 把m换成2,就可以AC爬楼梯这道题
                if (i - j >= 0) dp[i] += dp[i - j];
            }
        }
        return dp[n];
    }
};

代码中m表示最多可以爬m个台阶。

62.不同路径

分析可知,到达每一步,要么是从上方到达,要么是从左方到达,也就是说[i][j]的来源是[i-1][j]和[i][j-1],dp[i][j]的含义是到达[i][j]的路径条数,dp[i][j]=dp[i-1][j]+dp[i][j-1]【边界独立处理(左边和上边)】

整数拆分

dp[i]:对i拆分得到的最大的乘积。如果拆分v成2个数,j和i-j,那么继续拆分,就是j*dp[i-j]个数

⭐背包问题

掌握01背包和完全背包
给出一个总数,一些物品,问能否凑成这个总数。这是典型的背包问题!
一种物品只有取/不取两种状态

01背包

dp[i][j] 表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少
题目链接
递推公式dp[i][j]=max(dp[i-1,j],dp[i-1,j-weight[i]]+value[i])
c++万能头文件(在ACM模式中使用)#include
更好的方法是用滚动一维数组,一维dp遍历的时候,背包是从大到小,就是遍历顺序是j=bag,j–。并且遍历顺序只能是先遍历物品嵌套遍历背包容量

完全背包(同一件物品可以使用无数次)

01背包中的for循环,倒序遍历是为了让物品使用一次,那么改成正序遍历就可以使用无数次
完全背包二维DP数组的递推公式为:dp[i][j] = max(dp[i - 1][j], dp[i][j - weight[i]] + value[i])
一维递推:dp[j]=dp[j]+dp[j-coins[i]]

求组合与求排列的遍历顺序是不同的
如果求组合数就是外层for循环遍历物品,内层for遍历背包。
如果求排列数就是外层for遍历背包,内层for循环遍历物品。
总结:
求组合数:动态规划:518.零钱兑换II (opens new window)求排列数:动态规划:377. 组合总和 Ⅳ (opens new window)、动态规划:70. 爬楼梯进阶版(完全背包) (opens new window)求最小数:动态规划:322. 零钱兑换 (opens new window)、动态规划:279.完全平方数

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

华为
能否分成两个和相等的集合,就是看是否存在和为sum/2的子数组
本题中每一个元素的数值既是重量,也是价值

1049.最后一块石头的重量II(01背包)

字节笔试
就是看能否尽量将石头分成两堆重量尽可能相同的子堆,然后和416分割等和子集就;类似了

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

注意组合和排列方式的代码区别
组合数:

for (int i = 0; i < coins.size(); i++) { // 遍历物品
    for (int j = coins[i]; j <= amount; j++) { // 遍历背包容量
        dp[j] += dp[j - coins[i]];
    }
}

排列数:

for (int j = 0; j <= amount; j++) { // 遍历背包容量
    for (int i = 0; i < coins.size(); i++) { // 遍历物品
        if (j - coins[i] >= 0) dp[j] += dp[j - coins[i]];
    }
}

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

排列类型

爬楼梯(进阶)

link

322. 零钱兑换

百度、wxg、pdd
动规五步:
① dp数组含义:凑成总金额所需的 最少的硬币个数
② 递推公式关系:dp[j] = min(dp[j - coins[i]] + 1, dp[j]);解释:每遍历一个coins的元素,dp[j]都会更新,更新之后是dp[j]=(dp[j-coins[i])+1,需要和之前的对比找出更小的
③ 初始化:因为是找最小的。所以初始化为一个最大的数INT_MAX
④ 遍历顺序:先物品后背包
⑤ dp数组举例推导

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        vector<int> dp(amount+1,INT_MAX);//注意是amount+1,不然会报错
        dp[0]=0;
        for(int i=0;i<coins.size();i++){
            for(int j=coins[i];j<=amount;j++){
                if(dp[j-coins[i]]!=INT_MAX){//如果是初始值则跳过,因为如果是初始值,证明无法凑成当前的amount
                    dp[j]=min(dp[j-coins[i]]+1,dp[j]);
                }
            }
        }
        if(dp[amount]==INT_MAX) return -1;
        return dp[amount];
    }
};

279.完全平方数

huawei,百度,美团
总数就是和–背包;每个完全平方数就是物品
完全平方数就是物品(可以无限件使用),凑个正整数n就是背包,问凑满这个背包最少有多少物品
h和零钱兑换是一样的

class Solution {
public:
    int numSquares(int n) {
        vector<int> dp(n+1,INT_MAX);
        dp[0]=0;
        for(int i=1;i*i<n;i++){//注意起始i从1开始
            for(int j=i*i;j<=n;j++){//注意起始j从i*i开始
                dp[j]=min(dp[j-i*i]+1,dp[j]);
            }
        }
        return dp[n];
    }
};

139. 单词拆分

hauwei/字节

  1. dp[i]为treu,或false
  2. 递推:若s.substr[j,i-j]在wordset中且dp[j]==treu,则dp[i]=true;
  3. 遍历顺序,由于字符串具有先后顺序,所以是排列问题而不是组合问题,所以要先背包再物品
  4. 初始化:初始都是false,但dp[0]需要是true
class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        vector<bool> dp(s.size()+1,false);
        dp[0]=true;
        for(int i = 1;i<=s.size();i++){//先背包,注意需要<=而不是<
            for(int j=0;j<i;j++){
                string substr = s.substr(j,i-j);
                if(dp[j]&&find(wordDict.begin(),wordDict.end(), substr)!=wordDict.end()){
                    dp[i] = true;
                }
            }
        }
        return dp[s.size()];
    }
};

你可能感兴趣的:(代码随想录跟练记录,算法,c++,力扣,数据结构,开发语言)