一. Unique Paths
A robot is located at the top-left corner of a m x n grid (marked ‘Start’ in the diagram below).
The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked ‘Finish’ in the diagram below).
How many possible unique paths are there?
Above is a 3 x 7 grid. How many possible unique paths are there?
Note: m and n will be at most 100.
Difficulty:Medium
TIME:12MIN
解法一(动态规划)
这道题,按题目说的数据量,如果采用搜索来做,超时肯定超到天边去了。
这道题可以用动态规划来求解:
- 如果只有一行,那么当然只有一条路可以走,那么可以设定一个数组为[1,1,1,1,1,1,1],表示从该点到终点的所有可能路径数。
- 如果有两行,我们会发现,第一行的路径数变为了[7,6,5,4,3,2,1],为下面那一行所有可能路径数的叠加值。
- 同理,如果有三行,那么第一行的路径就是第二行路径的叠加值,而第二行路径为最后一行路径的叠加值。
因此,代码如下:
int uniquePaths(int m, int n) {
vector<int> v(n,1);
while(m > 1) {
for(int i = 1; i < n; i++)
v[i] += v[i - 1];
m--;
}
return v[n - 1];
}
代码的时间复杂度为 O(mn) 。
解法二(公式求解)
既然每次向右走和每次向下走的选择都会创造一条独一无二的路径,那么我们可以把行走的路径看出一个排列
比如其中一条路径就可以表示为[D,D,R,R,R,R,R,R],就是一直往下走,到底后就一直往右走。而且,任意两个D和六个R的排列,都是一条独一无二的路径,所以,我们只要求出了两个D和六个R的所有排列数,就相当于求解了3 x 7 grid的所有路径数。
求全排列总数的公式为 (m+n)!/(m!∗n!) 。
int uniquePaths(int m, int n) {
int big = max(m - 1, n - 1);
int small = min(m - 1, n - 1);
long result = 1;
for(int i = 1, j = big + 1; i <= small; i++, j++) {
result *= j;
result /= i;
}
return (int)result;
}
代码的时间复杂度为 O(min(n,m)) 。
二. Unique Paths II
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 as 1 and 0 respectively 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 is 2.
Note: m and n will be at most 100.
Difficulty:Medium
TIME:16MIN
解法(动态规划)
题目改变之后应该是不能用公式来直接求解了,不过还是可以用动态规划来求解。
上面说过是求叠加值,那是因为第一题是第二题的一种特例,其实这道题的最优子结构具体来说可以描述为:
- dp[i][j] = dp[i+1][j]+dp[i][j+1];
也就是往右边走以及往下走的所有路径之和,而且如果该点为障碍,那么就直接把值设为0即可。
其实这道题自己创建个数组可能会比较简单(就是创建m+1行n+1列的数组),这样处理的边界情况会少很多,不过为了节省空间,我还是使用了输入的数组作为动态规划的数组。
int uniquePathsWithObstacles(vector<vector<int>>& grid) {
int m = grid.size();
int n = grid[0].size();
/*初始化最后一行*/
grid[m - 1][n - 1] = 1 - grid[m - 1][n - 1];
for(int i = n - 2; i >= 0; i--) {
grid[m - 1][i] = grid[m - 1][i] != 1 ? grid[m - 1][i + 1] : 0;
}
for(int i = m - 2; i >= 0; i--) {
/*初始化该行最后一个点*/
grid[i][n - 1] = grid[i][n - 1] != 1 ? grid[i + 1][n - 1] : 0;
for(int j = n - 2; j >= 0; j--) {
//dp过程
grid[i][j] = grid[i][j] != 1 ? (grid[i][j + 1] + grid[i + 1][j]) : 0;
}
}
return grid[0][0];
}
代码的时间复杂度为 O(mn) 。