思路:动态规划,很明显在(x,y)到终点的位置的方法数目就是(x, y + 1)【向右走到的点去终点的路径数目】 + (x + 1, y)【向下走到的点去终点的路径数目】
首先一个超时的递归版本
class Solution {
public:
int uniquePaths(int m, int n) {
int res = solve(0 , 0, m ,n );
return res;
}
int solve(int x, int y, int m, int n)
{
if(x == m - 1 &&y == n - 1)//走到终点了
return 1;
if(x <0 || x > m || y < 0 || y > n)//越界了
return 0;
//就是右边点到终点的路径数+下边点到终点的路径数
return solve(x + 1, y, m, n) + solve(x, y + 1, m, n );
}
};
显然对上面之所以超时是因为有一些点到终点的方法数重复去求解了,比如求(0,1)到终点(3,3)的方法数时,其下角的点(1,2)到终点的方法数求一次((0 ,1 )先走到右边 (0, 2 ),(0,2)走下面(1 , 2 ))
然后再求(0,2)到终点方法数目时候,走下边(1,2),(1, 2)又被求了一次。诸如这样重复计算的点还有很多
所以可以在求到每次的结果的时候可以把结果先储存起来。就是记忆化递归了。
记忆化递归版本:
class Solution {
public:
int uniquePaths(int m, int n) {
//储存(x, y)到终点路径数量
vector<vector<int > >f (m +1, vector<int>(n + 1) );
//标记(x, y)到终点路径数量是否已经求出来了
vector<vector<bool> >mark(m + 1, vector<bool>(n + 1) );
//求解
int res = solve(0 , 0, m ,n , f, mark);
return res;
}
int solve(int x, int y, int m, int n, vector<vector<int> > & f, vector<vector<bool> > & mark)
{
if(x == m - 1 && y == n - 1)//走到终点了
{
mark[x][y] = true;//标记该点到终点路径数目已经求出来了
f[x][y] = 1;//储存结果
return 1;
}
if(x <0 || x > m || y < 0 || y > n)//越界了
{
return 0;
}
if(x +1 >= 0 && x +1 < m && !mark[x +1][y])//在范围内且没被求过
{
f[x + 1][y] = solve(x +1, y, m, n, f, mark);//储存结果
mark[x +1][y] = true;//标记
}
if(y + 1 >= 0 && y + 1 < n && !mark[x][y +1])//同上
{
f[x][y + 1] = solve(x, y + 1, m, n, f, mark);
mark[x][y + 1] = true;
}
return f[x + 1][y] + f[x][ y + 1]; //就是右边的点到终点的路径数+下边的点到终点的路径数
}
};
动态规划版本。其实是记忆化递归的逆过程。
class Solution {
public:
int uniquePaths(int m, int n) {
if(m <= 0 ||n <= 0)
return 0;
vector<vector<int> >f(m, vector<int>(n));//用向量表示二维数组m*n
//也可以用下面的动态申请二维数组来表示m*n数组
/*
int ** f;
f = new int*[m +1];
for(int i = 0; i < m +1; i++)
f[i] = new int[n +1];
*/
for(int i = 0; i < m ; i++)//初始化所有的点到终点路径数目
{
for(int j = 0; j < n ;j++)
f[i][j] = 1;
}
for(int j = 0; j < n - 1; j++)//初始化底边到终点路径数目
f[m-1][j] = 1;
for(int i = 0; i < m - 1 ;i++)//初始化最右边到终点路径数目
f[i][n - 1] = 1;
for(int i = m - 2; i >= 0; i--)
{
for(int j = n - 2; j >= 0; j--)
f[i][j] = f[i +1][j] + f[i][j + 1];//(i,j)到终点路径数就是其右边的点到终点的路径数 + 其下面的点到终点的路径数
}
return f[0][0];//起点到终点的路径数目
}
};