【LeetCode】不同路径
如图,m × n 的网格的左上角作为起点,每次只能向右或向下移动一格,最终要到达右下角。求有多少条可能的路径。

m,n 最大取 100。
我的想法是递归,分分钟实现
int uniquePaths(int m, int n) {
if (m == || n == ) return ;
return uniquePaths(m - , n) + uniquePaths(m, n - );
}
然而数字稍微取大(m = 19,n = 13)就 Time Limit Exceeded 了。
于是这么想,以上图 3 × 7 的网格为例,可以向下移动 2 格,向右移动 6 格,一共需要移动 8 格。那么只需要在 8 格中任意挑选 2 格作为向下移动,另外 6 格都向右即可。每种不同的选择表示了一条不同的路径。

那么可以用排列组合公式。m 和 n 表示向右或向下移动的次数,第二个等号是为了减少计算次数,因为 m!、n!、(m+n)! 阶乘计算时重复计算了一些数字。

int uniquePaths(int m, int n) {
m--; n--;
int small = min(m, n), big = max(m, n);
return fac(big + , big + small) / fac(, small);
}
/* 由于m、n能取到100,使用int计算连乘会溢出 */
long long fac(int start, int end) {
long long result = ;
for (int i = start; i <= end; i++)
result *= i;
return result;
}
同时注意到公式还可以继续化简,这样就只需要用一个循环语句。

但这种算法用到了 n 次除法,得注意将计算结果存储在 double 型变量中,因为 int 型的 (m + i) / i 会得出一个整数结果而导致计算过程中数据错误。
如果定义 int result = 1,第 6 行为 result *= (big + i) / i,计算会出错。例如 m = 4,n = 4 时,会输出 16,而正确答案是 20。
即使将 result 改为 double 型,第 6 行改为 result *= (double)(big + i) / i,也会出错。例如 m = 10,n = 10 时,会输出 48619,而正确答案是 48620。
int uniquePaths(int m, int n) {
m--; n--;
int small = min(m, n), big = max(m, n);
double result = 1.0;
for (int i = ; i <= small; i++) {
result = result * (big + i) / i;
}
return (int)result;
}
因此输入输出均为 int 型的计算中,若计算过程用到多次很可能无法除尽的除法时,得非常小心。
方法二:
动态规划
这是一个基本的动态规划问题。
由于只能向右或向下移动,那么到达一个格子的时候只有可能是两种情况:
- 从上边一格向下移动到这一格;
- 从左边一格向右移动到这一格。
假设移动到 (i, j) 这一格的不同路径数为 P[i][j],显然,P[i][j] = P[i - 1][j] + P[i][j - 1]。边界条件是最左边一列(无法从更左边移动过来)和最上边一行(无法从更上边移动过来),但显然对于所有 i,j 有 P[0][j] = 1,P[i][0] = 1。
int uniquePaths(int m, int n) {
vector<vector<int>> path(m, vector<int> (n, ));
for (int i = ; i < m; i++)
for (int j = ; j < n; j++)
path[i][j] = path[i - ][j] + path[i][j - ];
return path[m - ][n - ];
}
这种算法的时间复杂度是 O(m * n),空间复杂度也是 O(m * n),效率较低。
注意到每次更新 P[i][j] 的值,只需要用到 P[i - 1][j](同一列)和 P[i][j - 1](左边一列),因此只需要维护两列元素而不需要维护整个 m × n 矩阵。
int uniquePaths(int m, int n) {
if (m > n) return uniquePaths(n, m); // 这种方法比比较 m、n 大小并交换或者取较大、较小值更高明!
vector<int> left(n, ), right(n, );
for (int i = ; i < m; i++) {
for (int j = ; j < n; j++)
right[j] = left[j] + right[j - ];
swap(left, right);
}
return left[n - ];
}
这种算法的空间复杂度优化到 O(min(m, n))。又发现,对两列执行完循环后移动到下面两列时,左边一列 left 就是上一轮 right 交换过来的,因此只需要维护一列即可。
int uniquePaths(int m, int n) {
if (m > n) return uniquePaths(n, m);
vector<int> path(n, );
for (int i = ; i < m; i++) {
for (int j = ; j < n; j++)
path[j] += path[j - ];
}
return path[n - ];
}
【LeetCode】不同路径的更多相关文章
- LeetCode:路径总和II【113】
LeetCode:路径总和II[113] 题目描述 给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径. 说明: 叶子节点是指没有子节点的节点. 示例:给定如下二叉树, ...
- LeetCode:路径总和【112】
LeetCode:路径总和[112] 题目描述 给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和. 说明: 叶子节点是指没有子节点的节点. 示例 ...
- Leetcode 不同路径系列
Leetcode不同路径系列题解笔记 62. 不同路径 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 "Start" ). 机器人每次只能向下或者向右移动一 ...
- leetcode不同路径
62. 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” ). 机器人每次只能向下或者向右移动一步.机器人试图达到网格的右下角(在下图中标记为“Finish”). 问 ...
- LeetCode 112. 路径总和(Path Sum) 10
112. 路径总和 112. Path Sum 题目描述 给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和. 说明: 叶子节点是指没有子节点的节 ...
- LeetCode(2)---路径总和
给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和. 说明: 叶子节点是指没有子节点的节点. 示例: 给定如下二叉树,以及目标和 sum = ...
- LeetCode 112.路径总和(C++)
给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和. 说明: 叶子节点是指没有子节点的节点. 示例: 给定如下二叉树,以及目标和 sum = 22 ...
- LeetCode 中级 - 路径总和2(113)
给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径. 说明: 叶子节点是指没有子节点的节点. 示例:给定如下二叉树,以及目标和 sum = 22, 5 / \ 4 8 ...
- LeetCode 简单 - 路径总和(112)
给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和. 说明: 叶子节点是指没有子节点的节点. 示例: 给定如下二叉树,以及目标和 sum = 22 ...
- leetcode 437. 路径总和 III
题目描述: 给定一个二叉树,它的每个结点都存放着一个整数值. 找出路径和等于给定数值的路径总数. 路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点). 二 ...
随机推荐
- Java-idea-mybatis plugin插件使用
方案一.免费插件[推荐] Free Mybatis plugin 方案二.破解插件 安装路径 File→Setting→plugin→Install plugin 搜索需要插件即可 搜索Mybati ...
- node os模块
const os = require('os'); console.log(os.homedir()); console.log(os.hostname()); console.log(os.plat ...
- k-means性能测试
clf = MiniBatchKMeans(n_clusters=5000, batch_size=5000, n_init=1, max_iter=200, max_no_improvement=1 ...
- Parco_Love_gcd
传送门 出题人说正解为RMQ,鄙人实在太蒟蒻了,不会呀只能暴力…… #include <bits/stdc++.h> using namespace std; #define ll lon ...
- 56.关于vue项目的seo问题
不可否定的是,vue现在火.但是在实际项目中,特别是像一下交互网站,我们不可避免会考虑到的是seo问题,这直接关系到我们网站的排名,很多人说用vue搭建的网站不能做优化,那我们真的要放弃vue,放弃前 ...
- 5.Python3程序结构
5.1顺序结构 一条语句一条语句顺序的执行. 5.2选择结构 条件语句是通过一条或多条语句的执行结果(True或者False)来决定执行的代码块. 可以通过下图来简单了解条件语句的执行过程: 5.2. ...
- week_one-python基础 列表 增删改查
# Author:larlly #列表增删改查#定义列表name = ["wo","ni","ta","wo"] #定义 ...
- 极路由hosts
cat /etc/hosts 127.0.0.1 localhost tw-vars hiwifi 192.168.199.1 t.w tw t 4006024680.com www.40060246 ...
- kubeflow 创建tensorflow过程
online deployable ,install k8s 代码 Kubeflow有三个核心组件 TFJob Operator 和 Controller: 作为Kubernetes的扩展,来简化分布 ...
- JS设计模式(3)代理模式
什么是代理模式? 情景:小明追女生 A 非代理模式:小明 =花=> 女生A 代理模式:小明 =花=> 让女生A的好友B帮忙 =花=> 女生A 定义:为其他对象提供一种代理以控制对这个 ...