Unique Paths

描述

A robot is located at the top-left corner of a m × 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 × 7` grid. How many possible unique paths are there?

Figure: Above is a 3 × 7 grid. How many possible unique paths are there?

Note: m and n will be at most 100.

深搜

  1. // Unique Paths
  2. // 深搜,小集合可以过,大集合会超时
  3. // 时间复杂度O(n^4),空间复杂度O(n)
  4. class Solution {
  5. public:
  6. int uniquePaths(int m, int n) {
  7. if (m < 1 || n < 1) return 0; // 终止条件
  8. if (m == 1 && n == 1) return 1; // 收敛条件
  9. return uniquePaths(m - 1, n) + uniquePaths(m, n - 1);
  10. }
  11. };

备忘录法

给前面的深搜,加个缓存,就可以过大集合了。即备忘录法。

  1. // Unique Paths
  2. // 深搜 + 缓存,即备忘录法
  3. // 时间复杂度O(n^2),空间复杂度O(n^2)
  4. class Solution {
  5. public:
  6. int uniquePaths(int m, int n) {
  7. // f[x][y] 表示 从(0,0)到(x,y)的路径条数
  8. f = vector<vector<int> >(m, vector<int>(n, 0));
  9. f[0][0] = 1;
  10. return dfs(m - 1, n - 1);
  11. }
  12. private:
  13. vector<vector<int> > f; // 缓存
  14. int dfs(int x, int y) {
  15. if (x < 0 || y < 0) return 0; // 数据非法,终止条件
  16. if (x == 0 && y == 0) return f[0][0]; // 回到起点,收敛条件
  17. if (f[x][y] > 0) {
  18. return f[x][y];
  19. } else {
  20. return f[x][y] = dfs(x - 1, y) + dfs(x, y - 1);
  21. }
  22. }
  23. };

动规

既然可以用备忘录法自顶向下解决,也一定可以用动规自底向上解决。

设状态为f[i][j],表示从起点(1,1)到达(i,j)的路线条数,则状态转移方程为:

  1. f[i][j]=f[i-1][j]+f[i][j-1]
  1. // Unique Paths
  2. // 动规,滚动数组
  3. // 时间复杂度O(n^2),空间复杂度O(n)
  4. class Solution {
  5. public:
  6. int uniquePaths(int m, int n) {
  7. vector<int> f(n, 0);
  8. f[0] = 1;
  9. for (int i = 0; i < m; i++) {
  10. for (int j = 1; j < n; j++) {
  11. // 左边的f[j],表示更新后的f[j],与公式中的f[i][j]对应
  12. // 右边的f[j],表示老的f[j],与公式中的f[i-1][j]对应
  13. f[j] = f[j] + f[j - 1];
  14. }
  15. }
  16. return f[n - 1];
  17. }
  18. };

数学公式

一个m行,n列的矩阵,机器人从左上走到右下总共需要的步数是m+n-2,其中向下走的步数是m-1,因此问题变成了在m+n-2个操作中,选择m–1个时间点向下走,选择方式有多少种。即 Cm+n2m1C_{m+n-2}^{m-1}

  1. // LeetCode, Unique Paths
  2. // 数学公式
  3. class Solution {
  4. public:
  5. typedef long long int64_t;
  6. // 求阶乘, n!/(start-1)!,即 n*(n-1)...start,要求 n >= 1
  7. static int64_t factor(int n, int start = 1) {
  8. int64_t ret = 1;
  9. for(int i = start; i <= n; ++i)
  10. ret *= i;
  11. return ret;
  12. }
  13. // 求组合数 C_n^k
  14. static int64_t combination(int n, int k) {
  15. // 常数优化
  16. if (k == 0) return 1;
  17. if (k == 1) return n;
  18. int64_t ret = factor(n, k+1);
  19. ret /= factor(n - k);
  20. return ret;
  21. }
  22. int uniquePaths(int m, int n) {
  23. // max 可以防止n和k差距过大,从而防止combination()溢出
  24. return combination(m+n-2, max(m-1, n-1));
  25. }
  26. };

相关题目

原文: https://soulmachine.gitbooks.io/algorithm-essentials/content/cpp/dfs/unique-paths.html