剑指offer刷题动态规划

Posted 非晚非晚

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了剑指offer刷题动态规划相关的知识,希望对你有一定的参考价值。

记录在Leetcode刷《剑指offer》的笔记,希望提高自己的算法基础和编程水平。这一篇文章刷的是动态规划的题目集合,在CSDN做一下记录,随时更新,一起学习吧。

动态规划过程是:每次决策依赖于当前状态,又随即引起状态的转移。一个决策序列就是在变化的状态中产生出来的,所以,这种多阶段最优化决策解决问题的过程就称为动态规划。

一般它的步骤分为下列四步:

  1. 状态定义
  2. 转移方程
  3. 初始化
  4. 返回值

刷题链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/55c0wg/

1. 剑指 Offer 10- I. 斐波那契数列

  • 题目

在这里插入图片描述

  • 提交答案

思路:普通循环(递归都可以转化为循环),每次计算前两个元素的加法值即可。要注意提示的取余操作,因为最多要做100次,所以必须在for循环内取余。

f(n + 1) = f(n) + f(n - 1)为转移方程。

class Solution {
public:
    int fib(int n) {
        if(n ==0 || n == 1)return n;
        int fib_1 = 0;
        int fib_2 = 1;
        int temp = 0;
        for(int i = 1; i < n; i++)
        {
            temp = fib_2;
            fib_2 += fib_1;
            fib_2 %=1000000007;
            fib_1 = temp;
        }
        return fib_2;
    }
};
  • 题目解析

状态转移,3个变量轮流转移值。

class Solution {
public:
    int fib(int n) {
        int a = 0, b = 1, sum;
        for(int i = 0; i < n; i++){
            sum = (a + b) % 1000000007;
            a = b;
            b = sum;
        }
        return a;
    }
};

2. 剑指 Offer 10- II. 青蛙跳台阶问题

  • 题目

在这里插入图片描述

  • 题目解析

思路:也是斐波那契数列问题。设跳上 n级台阶有 f(n) 种跳法,n - 1级台阶有f(n-1)中跳法,所以n+1级台阶就是n和n-1级方法的和。

  • 当跳1级台阶: 剩 n-1个台阶,此情况共有 f(n-1)种跳法
  • 当跳2级台阶: 剩 n-2 个台阶,此情况共有 f(n-2) 种跳法。
  • 即 f(n) 为以上两种情况之和,即 f(n)=f(n-1)+f(n-2)
  • 提交答案
class Solution {
public:
    int numWays(int n) {
        if(n== 0 || n== 1)return 1;
       int a = 1;
       int b = 1;
       int sum = 0;
       for(int i = 2; i <=n; i++)
       {
           sum = (a + b)%1000000007;
           a = b;
           b = sum;
       }
       return sum;

3. 剑指 Offer 19. 正则表达式匹配

  • 题目
    在这里插入图片描述

  • 题目解析
    思路:
    在这里插入图片描述

class Solution {
public:
    bool isMatch(string s, string p) {
        int m = s.size() + 1, n = p.size() + 1;
        vector<vector<bool>> dp(m, vector<bool>(n, false));
        dp[0][0] = true;
        // 初始化首行
        for(int j = 2; j < n; j += 2)
            dp[0][j] = dp[0][j - 2] && p[j - 1] == '*';
        // 状态转移
        for(int i = 1; i < m; i++) {
            for(int j = 1; j < n; j++) {
                if(p[j - 1] == '*') {
                    if(dp[i][j - 2]) dp[i][j] = true;                              // 1.
                    else if(dp[i - 1][j] && s[i - 1] == p[j - 2]) dp[i][j] = true; // 2.
                    else if(dp[i - 1][j] && p[j - 2] == '.') dp[i][j] = true;      // 3.
                } else {
                    if(dp[i - 1][j - 1] && s[i - 1] == p[j - 1]) dp[i][j] = true;  // 1.
                    else if(dp[i - 1][j - 1] && p[j - 1] == '.') dp[i][j] = true;  // 2.
                }
            }
        }
        return dp[m - 1][n - 1];
    }
};

4. 剑指 Offer 42. 连续子数组的最大和

  • 题目

在这里插入图片描述

  • 题目解析

思路:

  1. 状态定义:动态规则列表dp,dp[i]表示以元素num[i]结尾的连续子数组最大和。
  2. 转移方程:分dp[i - 1]是否大于0,对dp[i]是否做贡献。
  3. 初始状态:dp[0] = nums[0].
  4. 返回值:返回dp列表中的最大值。
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int res = nums[0];
        for(int i = 1; i < nums.size(); i++) {
            if(nums[i - 1] > 0) nums[i] += nums[i - 1];//num[i-1]是否有贡献
            if(nums[i] > res) res = nums[i];//加入后的数据是否有贡献
        }
        return res;  
    }
};

5. 剑指 Offer 46. 把数字翻译成字符串

  • 题目

在这里插入图片描述

  • 提交结果

思路:从个位数开始,依次判断状态。根据上一个状态是单独翻译,还是组合翻译进行分类。

class Solution {
public:
    int translateNum(int num) {
        if( num < 10)
        return 1;
        int pair = 0;
        int single = 1;//预算一个元素,从第二个元素开始
        int n = num%10;//保留一个将要删除的元素
        int temp;
        while(num/10 != 0)
        {
            num = num / 10;//删除元素
            if( (num % 10 > 2) || (num%10 == 0) || ((num % 10 == 2) && (n > 5))) {single = pair + single; pair = 0;}//必须独立翻译
            else {temp = single; single = pair + single; pair = temp; }
            n = num%10;//取余
        }
            return single + pair;
    }
};
  • 题目解析

思路:字符串方案

class Solution {
public:
    int translateNum(int num) {
        string s = to_string(num);
        int a = 1, b = 1, len = s.size();
        for(int i = 2; i <= len; i++) {
            string tmp = s.substr(i - 2, 2);
            int c = tmp.compare("10") >= 0 && tmp.compare("25") <= 0 ? a + b : a;
            b = a;
            a = c;
        }
        return a;
    }
};

6. 剑指 Offer 47. 礼物的最大价值

  • 题目
    在这里插入图片描述
  • 提交答案

思路:斜线开始依次遍历,不过看了题目解析,其实没必要。。。。

class Solution {
public:
    int maxValue(vector<vector<int>>& grid) {
        int m = grid.size();
        int n = grid[0].size();
        vector<vector<int>> s (m, vector<int>(n, 0));//m个向量
        s[0][0] = grid[0][0];//第一个元素不用计算
        if(m == 1 && n == 1)
            return s[0][0];
        int i = 0;
        int j = 0;
        for(int k = 1; k < (m + n - 1); k++)//每次增加一步
        {
            if(k > m - 1) {i = m -1;j = k - m + 1;}
            else{ i = k;j = 0;}
            for(; j <= k; i--,j++)
            {
                if(j >= n || i < 0) break;
                if(i == 0 ) s[i][j] = s[i ][j - 1];//只有一种路径
                else if(j == 0 ) s[i][j] = s[i - 1][j];
                else {s[i][j] = max(s[i][j-1], s[i-1][j]);}//两种路径
                s[i][j] += grid[i][j];
            }
        }
        return s[m-1][n-1];
    }
};
  • 题目解析

思路,直接遍历,边上的直接累加,中间的比较之后累加。

class Solution {
public:
    int maxValue(vector<vector<int>>& grid) {
        int m = grid.size(), n = grid[0].size();
        for(int i = 0; i < m; i++) {
            for(int j = 0; j < n; j++) {
                if(i == 0 && j == 0) continue;
                if(i == 0) grid[i][j] += grid[i][j - 1] ;
                else if(j == 0) grid[i][j] += grid[i - 1][j];
                else grid[i][j] += max(grid[i][j - 1], grid[i - 1][j]);
            }
        }
        return grid[m - 1][n - 1];
    }
};

7. 剑指 Offer 48. 最长不含重复字符的子字符串

  • 题目
    在这里插入图片描述
  • 提交答案

思路:遍历字符串,按照是否有共同元素分类进行,需要注意的是,两个判断都需要用break。

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int len = s.size();
        if(len == 0 || len == 1)return len;
        int begin = 0;
        int end = 1;
        int sum = 1;
        while(end < len)
        {
            for(int i = begin; i < end; i++)
            {
                if(s[end] == s[i])//相同元素
                {
                    sum = max(end - begin, sum);
                    begin = i + 1;
                    end++;
                    break;//跳出
                }
                else if(i == end - 1)//没有相同元素
                {
                    end++;
                    break;
                }
            }
        }
        sum = max(end - begin, sum);
        return sum;
    }
};
  • 题目解析

思路:哈斯表+线性遍历

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        unordered_map<char, int> dic;
        int res = 0, tmp = 0, len = s.size(), i;
        for(int j = 0; j < len; j++) {
            if(dic.find(s[j]) == dic.end()) i = - 1;
            else i = dic.find(s[j])->second; // 获取索引 i
            dic[s[j]] = j; // 更新哈希表
            tmp = tmp < j - i ? tmp + 1 : j - i; // dp[j - 1] -> dp[j]
            res = max(res, tmp); // max(dp[j - 1], dp[j])
        }
        return res;
    }
};

8. 剑指 Offer 49. 丑数

  • 题目
    在这里插入图片描述
  • 题目解析

思路:每个数都是2,3,5的乘积堆起来的。用a,b,c指向他们的乘积,每次更新其中最小值。

class Solution {
public:
    int nthUglyNumber(int n) {
        int a = 0, b = 0, c = 0;
        int dp[n];
        dp[0] = 1;
        for(int i = 1; i < n; i++) {
            int n2 = dp[a] * 2, n3 = dp[b] * 3, n5 = dp[c] * 5;
            dp[i] = min(min(n2, n3), n5);
            if(dp以上是关于剑指offer刷题动态规划的主要内容,如果未能解决你的问题,请参考以下文章

剑指offer刷题动态规划(自用)

leetcode刷题总目录

牛客剑指offer刷题记录

剑指offer——第二十九天(动态规划“困难”)

剑指Offer018 - 020 刷题笔记 回文 双指针DP中心扩展

写写代码系列013:剑指offer题目——连续子数组的最大和(动态规划)