代码随想录动态规划2

62.不同路径

代码随想录

视频讲解:动态规划中如何初始化很重要!| LeetCode:62.不同路径_哔哩哔哩_bilibili

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。

问总共有多少条不同的路径?

思路:首先需要一个二维数组记录坐标(i,j),再思考走到终点dp[m-1][n-1]是需要从哪两个方向过来,再思考如何初始化,必须把第一行第一列全部初始化为1,因为只有一种路径,dp[0][0]==1,

也是因为只有保持不动这一种方式

class Solution {
public:
    int uniquePaths(int m, int n) {
       vector<vector<int>>dp(m,vector<int>(n,0));//二维数组的定义
        for(int i=0;i<n;i++)
        {
            dp[0][i]=1;
        }
        for(int j=0;j<m;j++)
        {
            dp[j][0]=1;
        }//初始化第一行第一列为0,不能只初始化dp[0][0]
        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];
    }
};
  • 时间复杂度:O(m × n)      因为两层for循环
  • 空间复杂度:O(m × n)       因为使用二维数组

    63. 不同路径 II

    https://programmercarl.com/0063.%E4%B8%8D%E5%90%8C%E8%B7%AF%E5%BE%84II.htmlhttps://programmercarl.com/0063.%E4%B8%8D%E5%90%8C%E8%B7%AF%E5%BE%84II.html

    视频讲解:动态规划,这次遇到障碍了| LeetCode:63. 不同路径 II_哔哩哔哩_bilibili

    给定一个 m x n 的整数数组 grid。一个机器人初始位于 左上角(即 grid[0][0])。机器人尝试移动到 右下角(即 grid[m - 1][n - 1])。机器人每次只能向下或者向右移动一步。

    网格中的障碍物和空位置分别用 1 和 0 来表示。机器人的移动路径中不能包含 任何 有障碍物的方格。

    返回机器人能够到达右下角的不同路径数量。

    测试用例保证答案小于等于 2 * 109

    思路:与上面这道题思路相似,遍历障碍物二维数组,遇到障碍物,直接dp[i][j]=0,

    语法上注意点:该函数传入的为vector<vector<int>>& obstacleGrid,不用手动输入次二维数组的数值,直接获取m,n

    class Solution {
    public:
        int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
            int m=obstacleGrid.size();
            int n=obstacleGrid[0].size();//直接通过所传递数组进行读入
            vector<vector<int>>dp(m,vector<int>(n,0));
           if(obstacleGrid[0][0]==1||obstacleGrid[m-1][n-1]==1)return 0;
           
           dp[0][0]=1;//初始化
            
            for(int i=0;i<m;i++)
            {
                for(int j=0;j<n;j++)
                {
                    if(obstacleGrid[i][j]==1)dp[i][j]=0;//该点本身是障碍物,不能通过任何方式移动到障碍物上
                    else{
                    if(i>0) dp[i][j]+=dp[i-1][j];
                    if(j>0)  dp[i][j]+=dp[i][j-1];}
                }
            }
            return dp[m-1][n-1];
        }
    };
    class Solution {
    public:
        int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
            int m = obstacleGrid.size();
            int n = obstacleGrid[0].size();
            
            if (obstacleGrid[0][0] == 1 || obstacleGrid[m-1][n-1] == 1) return 0;
    
            vector<vector<int>> dp(m, vector<int>(n, 0));
    
            // 初始化第一列
            for (int i = 0; i < m; i++) {
                if (obstacleGrid[i][0] == 1) break; // 遇到障碍物,后续都无法通行
                dp[i][0] = 1;
            }
    
            // 初始化第一行
            for (int j = 0; j < n; j++) {
                if (obstacleGrid[0][j] == 1) break;
                dp[0][j] = 1;
            }
    
            // 计算路径数
            for (int i = 1; i < m; i++) {
                for (int j = 1; j < n; j++) {
                    if (obstacleGrid[i][j] == 1) {
                        dp[i][j] = 0;
                    } else {
                        dp[i][j] = dp[i-1][j] + dp[i][j-1];
                    }
                }
            }
    
            return dp[m-1][n-1];
        }
    };
    

    第二种解题方式重新初始化第一行,第一列,其余不变

    假设我们有如下的 obstacleGrid

    0  0  1  0
    0  1  0  0
    0  0  0  0
    初始化之后

    dp[i][j]变成:

    1  1  0  0
    1  0  0  0
    1  0  0  0

    表示遇到障碍物后停止初始化为1,保持为0,因为不可能有路径走到这里。

    1. 整数拆分 

    代码随想录

    视频讲解:动态规划,本题关键在于理解递推公式!| LeetCode:343. 整数拆分_哔哩哔哩_bilibili

    贪心算法,需要利用数学规律,尽可能拆分成更多的3,如果只余下4,则不进行拆分了

    class Solution {
    public:
        int integerBreak(int n) {
            if(n==2)return 1;
            if(n==3)return 2;
            if(n==4)return 4;
            int result=1;
            while(n>4)//表示等于4的话就不用再分了,*4比*3*1更大
            {
            n=n-3;
            result=result*3;
            }
    return result*n;
        }
    };
    采用动态规划
    思路难点:重点在于找到关系式,
    dp[i-j]表示对i-j进行进一步拆分寻找最大值
    class Solution {
    public:
        int integerBreak(int n) {
           vector<int>dp(n+1,0);//默认初始化为0,但最好显式初始化
           dp[2]=1;//dp[n]的含义是非常直接的,表示整数n的拆分后的乘积最大化,初始化
    
           for(int i=3;i<=n;i++)//表示从3开始,计算3的整数拆分最大值
           {
            for(int j=1;j<i;j++)//枚举法,表示n该如何拆
              {dp[i]=max(dp[i],max(j*(i-j),j*dp[i-j]));//max只能传递两个参数,使用两个max
            }}
           
           return dp[n];
        }
    };

    时间复杂度:O(n^2) 空间复杂度:O(n)

    1. .不同的二叉搜索树 

    代码随想录

    视频讲解:动态规划找到子状态之间的关系很重要!| LeetCode:96.不同的二叉搜索树_哔哩哔哩_bilibili

    给你一个整数 n ,求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。

    选定某个数 i 作为根

    BST 的特性要求,根节点 i 左侧的数 [1, 2, ..., i-1] 必须都在 左子树。 右侧的数 [i+1, i+2, ..., n] 必须都在 右子树

    假设我们选 i 作为根:

    左子树有 i-1 个节点(数值范围是 [1, 2, ..., i-1])。 右子树有 n-i 个节点(数值范围是 [i+1, i+2, ..., n])。 左右子树的结构是 相互独立的,可以分别构造。

    假设 n = 3

    我们有 1, 2, 3 三个数,分别尝试以它们为根:

    1 作为根

    • 左子树:f(0) = 1(空树)。
    • 右子树:[2, 3]f(2) = 2
    • 总数:f(0) * f(2) = 1 × 2 = 2

    2 作为根

    • 左子树:[1]f(1) = 1
    • 右子树:[3]f(1) = 1
    • 总数:f(1) * f(1) = 1 × 1 = 1

    3 作为根

    • 左子树:[1, 2]f(2) = 2
    • 右子树:f(0) = 1(空树)。
    • 总数:f(2) * f(0) = 2 × 1 = 2

    思路:枚举需要累加所有可能性,dp[0]=1,表示空树也是一种有效的bst树,不会导致后面丢失有效的方案

    class Solution {
    public:
        int numTrees(int n) {
            vector<int>dp(n+1,0);//明确含义,dp[n]表示n个节点的二叉搜索树有多少种
           dp[0]=1;//空树
           dp[1]=1;
            for(int i=2;i<=n;i++){//计算dp[n]
            for(int j=1;j<=i;j++)//枚举,以哪个节点为根节点
            {
             dp[i]+=dp[j-1]*dp[i-j];//左子树的可能性乘右子树的可能性
            }
              
            }
    return dp[n];
        }
    };

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值