从起点搜到终点:
class Solution {
public:
int dfs (int x, int y, int m, int n)
{
if (x == m-1 && y == n-1){
return 1;
}
int sum=0;
if (x+1 <= m-1){
sum += dfs (x+1, y, m, n);
}
if (y+1 <= n-1){
sum += dfs (x, y+1, m, n);
}
return sum;
}
int uniquePaths(int m, int n) {
return dfs (0, 0, m, n);
}
};
上述暴搜的代码中不知道大家有没有观察到这么一个变量!即:
int sum=0;
sum += dfs (x+1, y, m, n);
sum += dfs (x, y+1, m, n);
sum记录的是从当前点到达终点的路径数;即从 (x,y)到达(m,n)的路径数目!那么我们可以开一个记忆化数组去记录当前点到达终点的路径数,毕竟sum是局部变量,只在当前函数内有效!
记忆化搜索的本质就是标记这个点的是否走过了,再走一次也是同样的结果!所以无需再走,直接查表,取出答案。去除重复!
注意:
关键点:本题的记忆化搜索是:加等于的形式,而有些记忆化搜索是 memo[x][y] = dfs (…);
memory[x][y] += dfs(x+1, y, m, n, memory);
memory[x][y] += dfs (x, y+1, m, n, memory);
两者的区别是:
+= :如图所示:紫色路线是之前走过的,而绿色路线是当前走的,现在绿色路线走到了之前已经走过的位置,而经过查表可知,这个位置到达终点的路径只有一条,所以直接返回1,因为从绿色路线去走到这个位置,再往下走的话,不也还是只有一条路线吗?何必呢?有人说如果查表得到的是3呢?即这个相遇位置到达终点的路线有3条,那还不是一样的吗,我绿色路线走到这个位置,然后从这个位置开始往下走,不也会走出同样的三条路线出来吗?何必呢?所以直接查表返回该点到终点的路径数目。
然后观察可得:绿色线和紫色线虽然从相遇点开始到终点的路线相同,但是之前的不同啊,只要有一个点不同,就不是一个方案:比如序列【1,2,1】和【2,1,1】是同一个方案吗??NO!所以说由于之前的路线不同,所以二者不是同一条路线,所以是 +=
!!!
class Solution {
public:
int dfs (int x, int y, int m, int n, vector<vector<int>>& memory)
{
if (memory[x][y] != 0)
return memory[x][y];
if (x == m-1 && y==n-1)
{
memory[x][y] = 1;
return memory[x][y];
}
if (x+1 <= m-1){
memory[x][y] += dfs(x+1, y, m, n, memory);
}
if (y+1 <= n-1){
memory[x][y] += dfs (x, y+1, m, n, memory);
}
return memory[x][y];
}
int uniquePaths(int m, int n) {
vector<vector<int>> memo(m + 1, vector<int>(n + 1, 0));
return dfs (0, 0, m, n, memo);
}
};
定义状态:dp[i][j] 表示从起点(0,0)到达(i,j)位置的路径数目。
状态转移:对于每个位置(i,j),可以从(i-1,j)或者(i,j-1)到达,因此到达(i,j)位置的路径数目为到达(i-1,j)位置的路径数目加上到达(i,j-1)位置的路径数目,即dp[i][j] = dp[i-1][j] + dp[i][j-1]。
边界条件:对于第一行和第一列上的每个位置,由于只能往右或往下走,因此到达这些位置只有一条路径,即dp[i][0] = dp[0][j] = 1。
最终结果:dp[m-1][n-1] 表示从起点到终点的路径数目。
class Solution {
public int uniquePaths(int m, int n) {
int[][] f = new int[m+1][n+1];
f[1][1] = 1;
for (int i=1; i <= m; i ++)
for (int j=1; j <= n; j ++)
if (i == 1 && j == 1)
continue;
else f[i][j] = f[i-1][j] + f[i][j-1];
return f[m][n];
}
}
推荐题解!