题意整理
- 给定一个的网格,求从起点(1,1)到终点(n,m)总共有多少不同的路劲。
- 规定每次只能向右走和向上走,并且不能走指定的矩形区域。
方法一(记忆化递归)
1.解题思路
- 递归终止条件:横坐标在1,纵坐标也在1时,递归终止,此时只有1条路径。
- 递归如何推进:每个位置的路径数可以由左边位置的情况得到,也可以由下边位置的情况得到,需要将这两种情况累加。
- 每层递归返回什么:返回当前位置的路径数。
由于朴素递归会有很多重复的计算,可以通过记忆数组来保存,从而避免不必要的重复计算。
2.代码实现
import java.util.*; public class Solution { /** * * @param n int整型 * @param m int整型 * @param x0 int整型 * @param y0 int整型 * @param x1 int整型 * @param y1 int整型 * @return int整型 */ //矩形边界 int x0,y0,x1,y1; //取余常数 int mod=1000000007; //记忆化数组 int[][] memo; public int GetNumberOfPath (int n, int m, int x0, int y0, int x1, int y1) { //初始化 this.x0=x0; this.y0=y0; this.x1=x1; this.y1=y1; memo=new int[n+1][m+1]; return path(n,m,n,m); } private int path(int n,int m,int i,int j){ //如果越界或者处于指定矩形区域,直接返回0 if(i<=0||j<=0||(i>=x0&&i<=x1&&j>=y0&&j<=y1)){ return 0; } //递归终止条件 if(i==1&&j==1) return 1; //如果记忆化数组中有,直接返回记忆化数组中存的值 if(memo[i][j]!=0) return memo[i][j]; //每次可以由左边过来,或者由下边过来 int cnt=(path(n,m,i-1,j)+path(n,m,i,j-1))%mod; memo[i][j]=cnt; return cnt; } }
3.复杂度分析
- 时间复杂度:行递归的深度为n,列递归的深度为m,所以时间复杂度为。
- 空间复杂度:需要额外大小为的记忆数组,所以空间复杂度为。
方法二(动态规划)
1.解题思路
- 状态定义:表示从起点走到处的路径条数。
- 状态初始化:当终点在处时,只有1条路径,所以。
- 状态转移:当前位置可以由左边位置过来,也可以由下边位置过来。所以。
动图展示:
2.代码实现
import java.util.*; public class Solution { /** * * @param n int整型 * @param m int整型 * @param x0 int整型 * @param y0 int整型 * @param x1 int整型 * @param y1 int整型 * @return int整型 */ //取余常数 int mod=1000000007; public int GetNumberOfPath (int n, int m, int x0, int y0, int x1, int y1) { //定义dp数组,dp[i][j]表示从起点走到(i,j)处的路径条数 int[][] dp=new int[n+1][m+1]; //赋初值 dp[1][1]=1; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ //如果处于矩形区域,直接跳过 if(i>=x0&&i<=x1&&j>=y0&&j<=y1){ continue; } //当前位置可以由左边位置过来 if(i>1){ dp[i][j]=(dp[i][j]+dp[i-1][j])%mod; } //当前位置可以由下边位置过来 if(j>1){ dp[i][j]=(dp[i][j]+dp[i][j-1])%mod; } } } return dp[n][m]; } }
3.复杂度分析
- 时间复杂度:循环最多遍历次,所以时间复杂度为。
- 空间复杂度:需要额外大小为的dp数组,所以空间复杂度为。