题目描述
解决方案
这是一个基本的DP问题。
由于机器人只能左右移动,当它到达一个点时,只有两种可能:
- 它从上面到达这一点(向下移动到这一点);
- 它从左到这一点(向右移动到这一点)。
因此,我们得到以下状态方程:假设到达点(i, j)的路径数记为P[i][j],则
P[i][j] = P[i - 1][j] + P[i][j - 1]
上述方程的边界条件出现在最左边的列(P[i][j - 1]不存在)和最上面的行(P[i - 1][j]不存在)。这些条件可以通过初始化(预处理)来处理——对于所有有效的i, j,初始化P[0][j] = 1, P[i][0] = 1。
现在我们可以写出以下(未优化的)代码。
class Solution {
int uniquePaths(int m, int n) {
vector<vector<int> > path(m, vector<int> (n, 1));
for (int i = 1; i < m; i++)
for (int j = 1; j < n; j++)
path[i][j] = path[i - 1][j] + path[i][j - 1];
return path[m - 1][n - 1];
}
};
上述方案的时间复杂度为O(n^2),空间复杂度为O(m * n)。但是,每次更新path[i][j]时,我们只需要path[i - 1]j和path[i]j - 1。因此,我们只需要维护两列(当前列和左列),而不是维护完整的m*n矩阵。现在可以对代码进行优化,使其具有O(min(m, n))的空间复杂度。
class Solution {
int uniquePaths(int m, int n) {
if (m > n) return uniquePaths(n, m);
vector<int> pre(m, 1);
vector<int> cur(m, 1);
for (int j = 1; j < n; j++) {
for (int i = 1; i < m; i++)
cur[i] = cur[i - 1] + pre[i];
swap(pre, cur);
}
return pre[m - 1];
}
};
进一步检查上面的代码,我们发现保留两列是用来恢复pre[i]的,pre[i]在更新之前是cur[i]。所以甚至不需要使用两个向量,一个就足够了。现在进一步节省了空间,代码也变得更短了。
class Solution {
int uniquePaths(int m, int n) {
if (m > n) return uniquePaths(n, m);
vector<int> cur(m, 1);
for (int j = 1; j < n; j++)
for (int i = 1; i < m; i++)
cur[i] += cur[i - 1];
return cur[m - 1];
}
};