LeetCode 63. 不同路径 II

题目:

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

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

现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?

网格中的障碍物和空位置分别用 1 和 0 来表示。

1. 实现算法

总的来说还是比较清晰,

    1. 确定dp数组(dp table)以及下标的含义
    2. 确定递推公式
    3. dp数组如何初始化
    4. 确定遍历顺序
    5. 举例推导dp数组

首先确定这个dp是一个二维数组,因为要考虑障碍,这些障碍压缩不到一行。

然后,确定与dp数组的每个格子代表行至此格的方法数,每个格子都是与真实网格obstacleGrid相对应的。

这一题的主要trick就是第一行第一列的障碍物后面必须是0。也就是说,只要遇到了一个障碍物,那在这之后的格子对应的每个路径都是0。在我的代码里,尝试过很多丑陋的写法,最终呈现如下方代码中的这一段所示:

 		if (obstacleGrid[0][0] == 1)
            return 0;
        dp[0][0] = 1;
        for(int i = 1; i < width; ++i)
        {
            if(obstacleGrid[0][i] != 1 && dp[0][i-1] != 0)
                dp[0][i] = 1;

        }

        for(int j = 1; j < lenth; ++j)
        {
            if(obstacleGrid[j][0] != 1 && dp[j-1][0] != 0)
                dp[j][0] = 1;
        }

在上面这段代码中,我依靠的是判断前一个格子的状态。

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) 
    {
       
        // 记录网格的长和宽
        int lenth = obstacleGrid.size();
        int width = obstacleGrid[0].size();
        
        // 新建与真实网格obstacleGrid对应的dp数组,每个格子代表行至此格的方法数
        int dp[lenth][width];
        memset(dp,0,sizeof(dp));
        //vector<vector<int>> dp(lenth, vector<int>(width, 0));

        if (obstacleGrid[0][0] == 1)
            return 0;
        dp[0][0] = 1;
        for(int i = 1; i < width; ++i)
        {
            if(obstacleGrid[0][i] != 1 && dp[0][i-1] != 0)
                dp[0][i] = 1;

        }

        for(int j = 1; j < lenth; ++j)
        {
            if(obstacleGrid[j][0] != 1 && dp[j-1][0] != 0)
                dp[j][0] = 1;
        }
        
        for(int i = 1; i < lenth; ++i)
        {
            for(int j = 1; j < width; ++j)
            {
                if(obstacleGrid[i][j] == 1)
                    dp[i][j] = 0;                
                else
                    dp[i][j] = dp[i-1][j] + dp[i][j-1];
            }
        }

        return dp[lenth-1][width-1];

    }
};

2. 优化

后来我看了一个解法,其他过程差不多,初始化第一行第一列那边就写了一点点:

for (int i = 0; i < m && obstacleGrid[i][0] == 0; i++) 
	dp[i][0] = 1;
for (int j = 0; j < n && obstacleGrid[0][j] == 0; j++) 
	dp[0][j] = 1;

刚开始看的时候觉得障碍物后面还是没考虑到,然后发现遇到障碍物,for循环直接停止了。

(注)3. int dp[][]和vector描述二维数组的区别

经常看到解答里面使用vector表示二维数组。
int dp[lenth][width];vector<vector<int>> dp(m, vector<int>(n, 0));的区别在于它们使用的底层数据结构不同。

第一个声明 int dp[lenth][width]; 声明了一个二维数组,使用的是 C++ 中的原生数组。这个数组的大小是在编译时就固定了的,无法动态改变大小。这也意味着当数组过大时,可能会导致堆栈溢出等问题。

第二个声明 vector<vector> dp(m, vector(n, 0)); 声明了一个二维向量,使用的是 C++ STL 中的 vector 容器。这个容器可以动态调整大小,并且可以在运行时根据需要分配更多的内存空间。使用 vector 容器可以避免 C++ 原生数组所遇到的问题,而且还提供了许多方便的方法和操作符,如 push_back、emplace_back、size、at 等。

因此,一般情况下,使用 vector 容器来代替原生数组是更为方便和安全的选择。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值