动态规划——unique-paths-ii

本文介绍了一种算法,用于计算在存在障碍的情况下从起点到终点的不同路径数量。通过使用递归和动态规划两种方法来解决这一问题,递归方法直接根据问题定义进行递归求解,而动态规划方法则构建一个矩阵逐步计算出最终结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述:

Follow up for "Unique Paths":

Now consider if some obstacles are added to the grids. How many unique paths would there be?

An obstacle and empty space is marked as1and0respectively in the grid.

For example,

There is one obstacle in the middle of a 3x3 grid as illustrated below.

[
  [0,0,0],
  [0,1,0],
  [0,0,0]
]

The total number of unique paths is2.

Note: m and n will be at most 100.

递归例程:
public class Solution {
    public int uniquePathsWithObstacles(int[][] grid) {
        if(grid == null||grid.length == 0||grid[0].length == 0)
            return 0;
        int row=grid.length;
        int col=grid[0].length;
        return help(grid,0,0,row,col);
    }
    public int help(int[][] grid,int i,int j,int row,int col)
        {
        if(i == row-1&&j == col-1)
            return 1;
        if(grid[i][j] == 1)
            return 0;
        if(i == row-1)
            return help(grid,i,j+1,row,col);
        if(j == col-1)
            return help(grid,i+1,j,row,col);
        else
            return help(grid,i+1,j,row,col)+help(grid,i,j+1,row,col);
    }
}


DP例程:
 public int uniquePathsWithObstacles(int[][] grid) {
        if(grid == null||grid.length == 0||grid[0].length == 0)
            return 0;
        int row=grid.length;
        int col=grid[0].length;
        int[][] dp=new int[row][col];
        dp[row-1][col-1]=1;
        if(grid[row-1][col-1] == 1)
            dp[row-1][col-1]=0;
        //最下行初始化
        for(int j=col-2;j>=0;j--)
            {
            if(grid[row-1][j] == 1)
                dp[row-1][j]=0;
            else
                dp[row-1][j]=dp[row-1][j+1];
        }
        //最右行初始化
        for(int i=row-2;i>=0;i--)
            {
            if(grid[i][col-1] == 1)
                dp[i][col-1]=0;
            else
                dp[i][col-1]=dp[i+1][col-1];
        }
        //general
        for(int i=row-2;i>=0;i--)
            {
            for(int j=col-2;j>=0;j--)
                {
                if(grid[i][j] == 1)
                    dp[i][j]=0;
                else
                    dp[i][j]=dp[i+1][j]+dp[i][j+1];
            }
        }
        return dp[0][0];
    }


### 动态规划算法及其 dp 数组的使用方法 动态规划(Dynamic Programming, DP)是一种通过将复杂问题分解为更小的子问题来解决优化问题的算法技术。它通常用于求解具有重叠子问题和最优子结构性质的问题。在动态规划中,`dp` 数组是一个关键的数据结构,用于存储子问题的解,避免重复计算。 #### 1. 动态规划的核心思想 动态规划的核心思想是将一个问题拆分为多个子问题,并通过递推关系从子问题的解逐步构建出原问题的解。这种思想可以显著减少计算量,提高算法效率[^1]。 #### 2. `dp` 数组的作用 `dp` 数组是用来存储子问题解的数组或矩阵。它的每个元素代表某个子问题的解。例如,在划分数组问题中,`dp[i][rest]` 表示在前 `i` 个元素中选择若干个数使得它们的累加和最接近但不超过 `rest` 的最大值[^1]。 #### 3. 动态规划的基本步骤 - **定义状态**:确定 `dp` 数组的含义,明确每个元素表示什么。 - **状态转移方程**:根据问题的性质,设计从一个状态转移到另一个状态的规则。 - **初始化**:设置 `dp` 数组的初始值,通常是边界条件。 - **遍历顺序**:按照正确的顺序填充 `dp` 数阵列,确保每个状态都可以从已经计算过的状态中得出。 - **返回结果**:从 `dp` 数组中提取最终答案。 #### 4. 示例代码解析 以下是一个经典的动态规划问题——计算不同路径的数量的代码解析: ```go func uniquePaths(m int, n int) int { dp := make([][]int, m) for k := range dp { dp[k] = make([]int, n) } for i := 0; i < m; i++ { dp[i][0] = 1 // 初始化第一列 } for j := 0; j < n; j++ { dp[0][j] = 1 // 初始化第一行 } for i := 1; i < m; i++ { for j := 1; j < n; j++ { dp[i][j] = dp[i-1][j] + dp[i][j-1] // 状态转移方程 } } return dp[m-1][n-1] // 返回最终结果 } ``` 在这个例子中,`dp[i][j]` 表示从左上角到 `(i, j)` 的不同路径数量。通过状态转移方程 `dp[i][j] = dp[i-1][j] + dp[i][j-1]`,我们可以从已知的状态推导出未知的状态[^3]。 #### 5. `dp` 函数与 `memo` 数组的关系 除了使用 `dp` 数组外,动态规划还可以通过递归结合记忆化搜索实现。在这种情况下,`memo` 数组用于存储递归过程中计算出的子问题解,避免重复计算。`memo` 数组的更新通常发生在递归函数的最后一步,确保每个子问题的解都是准确且完整的[^2]。 #### 6. 滚动数组优化 在一些动态规划问题中,`dp` 数组的空间复杂度可以通过滚动数组技术进行优化。例如,在二维 `dp` 数组中,如果状态转移只依赖于前一行的数据,则可以将二维数组压缩为一维数组,从而节省空间[^3]。 ### 总结 动态规划是一种强大的算法技术,其核心在于利用 `dp` 数组或 `memo` 数组存储子问题的解,避免重复计算。通过合理定义状态、设计状态转移方程以及初始化 `dp` 数组,可以高效地解决许多复杂的优化问题。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值