我在代码随想录|写代码Day33 | 动态规划| 路径问题| 62.不同路径,63. 不同路径 II,343. 整数拆分

🔥博客介绍`: 27dCnc

🎥系列专栏: <<数据结构与算法>> << 算法入门>> << C++项目>>

🎥 当前专栏: << 算法入门>>

专题 : 数据结构帮助小白快速入门算法

👍👍👍👍👍👍👍👍👍👍👍👍

☆*: .。. o(≧▽≦)o .。.:*☆

❤️感谢大家点赞👍收藏⭐评论✍️

学习目标:

今日学习打卡

  • 代码随想录 - 动态规划

    学习时间:

    • 周一至周五晚上 7 点—晚上9点
    • 周六上午 9 点-上午 11 点
    • 周日下午 3 点-下午 6 点

      学习内容:

      1. 不同路径
      2. 不同路径 II
      3. 整数拆分

      内容详细:

      62.不同路径

      考点: 动态规划 数学 深度优先搜索(dfs)

      解题思路

      高中时候的组合规律,当然我们不能直接这样写我们要进行动态规划分析

      首先看到题目是想到dfs

      class Solution {private:
          int dfs(int i, int j, int m, int n) { if (i > m || j > n) return 0; // 越界了
              if (i == m && j == n) return 1; // 找到一种方法,相当于找到了叶子节点
              return dfs(i + 1, j, m, n) + dfs(i, j + 1, m, n);
          }
      public:
          int uniquePaths(int m, int n) { return dfs(1, 1, m, n);
          }
      };
      

      但是超时

      开始动态规划

      1. 确定dp数组以及下标的含义
      dp[i][j] :表示从(0 ,0)出发,到(i, j) 有dp[i][j]条不同的路径。
      
      1. 确定递推公式(到这一步的方式)

      想要求dp[i][j],只能有两个方向来推导出来,即dp[i - 1][j] 和 dp[i][j - 1]。

      此时在回顾一下 dp[i - 1][j] 表示啥,是从(0, 0)的位置到(i - 1, j)有几条路径,dp[i][j - 1]同理。

      那么很自然,dp[i][j] = dp[i - 1][j] + dp[i][j - 1],因为dp[i][j]只有这两个方向过来。

      1. dp数组的初始化

        这个就是高中的知识点

      for (int i = 0; i < m; i++) dp[i][0] = 1;
      for (int j = 0; j < n; j++) dp[0][j] = 1;
      
      1. 确定遍历顺序

        这里要看一下递推公式dp[i][j] = dp[i - 1][j] + dp[i][j - 1],dp[i][j]都是从其上方和左方推导而来,那么从左到右一层一层遍历就可以了。

      这样就可以保证推导dp[i][j]的时候,dp[i - 1][j] 和 dp[i][j - 1]一定是有数值的。

      1. 举例推导dp数组

        最终代码

      class Solution {public:
          int uniquePaths(int m, int n) { int dp[m][n];
              dp[0][0] = 0;
              for (int i = 0; i < m; i++) dp[i][0] = 1;
              for (int j = 0; j < n; j++) dp[0][j] = 1;
              for (int i = 1;i < m; i++) { for (int j = 1; j < n; j++) { dp[i][j] = dp[i-1][j] + dp[i][j - 1];
                  }
              }
              return dp[m - 1][n - 1];
          }
      };
      

      63. 不同路径 II

      题目考点: 动态规划

      解题思路

      和上一题一样,只是多了障碍物,可以直接跳过,

      如果遇到障碍物 continue

      if (obstacleGrid[i][j] == 1) {continue;}
      

      i 代码

      class Solution {public:
          int uniquePathsWithObstacles(vector>& obstacleGrid) { int l,r;
              int m =obstacleGrid.size();
              int n = obstacleGrid[0].size();
              int dp[m][n];
              if (obstacleGrid[m - 1][n - 1] == 1 || obstacleGrid[0][0] == 1){ return 0;
              }
              memset(dp,0,sizeof(dp));
              for (int i = 0; i < m && !obstacleGrid[i][0]; i++) dp[i][0] = 1;
              for (int j = 0; j < n && !obstacleGrid[0][j]; j++) dp[0][j] = 1;
              for (int i = 1;i < m; i++) { for (int j = 1; j < n; j++) { if (obstacleGrid[i][j] == 1) {continue;}
                      dp[i][j] = dp[i-1][j] + dp[i][j - 1];
                  }
              }
              return dp[m - 1][n - 1];
          }
      };
      

      ii 代码

      class Solution {public:
          int uniquePathsWithObstacles(vector>& obstacleGrid) { if (obstacleGrid[0][0] == 1)
                  return 0;
              vector dp(obstacleGrid[0].size());
              for (int j = 0; j < dp.size(); ++j)
                  if (obstacleGrid[0][j] == 1)
                      dp[j] = 0;
                  else if (j == 0)
                      dp[j] = 1;
                  else
                      dp[j] = dp[j-1];
              for (int i = 1; i < obstacleGrid.size(); ++i)
                  for (int j = 0; j < dp.size(); ++j){ if (obstacleGrid[i][j] == 1)
                          dp[j] = 0;
                      else if (j != 0)
                          dp[j] = dp[j] + dp[j-1];
                  }
              return dp.back();
          }
      };
      

      343. 整数拆分

      题目考点: 动态规划

      解题思路

      可以拆分成多个数据对动态规划实行贪心,max就是贪心的具象化,(个人理解)

      其他步骤省,这里详细讲解

      • 确定递推公式

        可以想 dp[i]最大乘积是怎么得到的呢?

        其实可以从1遍历j,然后有两种渠道得到dp[i].

        一个是j * (i - j) 直接相乘。

        一个是j * dp[i - j],相当于是拆分(i - j),对这个拆分不理解的话,可以回想dp数组的定义。

        那有同学问了,j怎么就不拆分呢?

        j是从1开始遍历,拆分j的情况,在遍历j的过程中其实都计算过了。那么从1遍历j,比较(i - j) * j和dp[i - j] * j 取最大的。

        递推公式:

        dp[i] = max(dp[i], max((i - j) * j, dp[i - j] * j));
        

        也可以这么理解,j * (i - j) 是单纯的把整数拆分为两个数相乘,而j * dp[i - j]是拆分成两个以及两个以上的个数相乘。

        • 如果定义dp[i - j] * dp[j] 也是默认将一个数强制拆成4份以及4份以上了。

        • 所以递推公式:dp[i] = max({dp[i], (i - j) * j, dp[i - j] * j});

        • 那么在取最大值的时候,为什么还要比较dp[i]呢?

          因为在递推公式推导的过程中,每次计算dp[i],取最大的而已。

          class Solution {public:
              int integerBreak(int n) { if(n < 4) return n/2 * (n - n/2);
                  int dp[n+4];
                  memset(dp,0,sizeof dp);
                  dp[2] = 1;
                  for (int i = 3; i <= n ; i++) { for (int j = 1; j <= i / 2; j++) { dp[i] = max({dp[i], (i - j) * j, dp[i - j] * j});
                      }
                  }
                  return dp[n];
              }
          };
          

          学习产出:

          • 技术笔记 2 遍
          • CSDN 技术博客 3 篇
          • 习的 vlog 视频 1 个

            重磅消息:

            GTP - 4 最新版接入服务他来了 点击链接即可查看详细

            GTP - 4 搭建教程

            🔥如果此文对你有帮助的话,欢迎💗关注、👍点赞、⭐收藏、✍️评论,支持一下博主~