题目意思
- 给你一个二维矩阵,需要找出从左上角走到右下角的不同的方案数。每次只能向下或者向右移动。过程中只要有一步不一样这种走法就不一样。
思路分析
- 题目意思很简单,其实这是一个排列组合的经典的题目。只要用到高中知识。我们发现,每种方案走的步数其实是固定的。对于一个m行n列的矩阵,我们走的步数其实就是固定了走n+m-2步,其中,我们要选择n-1步向右边走,m-1步向下走。这就是一个很经典的排列组合问题,在n+m-2中选择n-1步向右走,剩下的自然就是向下走了。最终的答案就是
,等价于
,下面,我们就来介绍求组合数的三种方法。
解法一 循环相乘
- 我们通过推导可以得到如下的式子。
题目说了答案的范围一定在int范围内,所以我们可以直接循环得到,但是要注意的是阶乘可能很大,记得使用unsigned long long 防止爆long long。当然,这种做法还是比较局限的。
- 代码如下
- 循环一遍,时间复杂度为O(n)
- 只用了几个变量存储,空间复杂度为O(1)
class Solution {
public:
/**
*
* @param m int整型
* @param n int整型
* @return int整型
*/
typedef unsigned long long ll;
int uniquePaths(int m, int n) {
// write code here
ll ans=1;
// 转化为组合数进行求解
ll a=n+m-2,b=max(n-1,m-1);
if(b==0||b==a) return 1; // 特殊情况直接返回
// 按照推导的公式枚举,注意范围
for(ll i=a;i>b;i--){
ans=ans*i;
}
for(ll i=1;i<=(a-b);i++){
ans/=i;
}
return ans;
}
};解法二 递归写法
- 学过数学的同学基本都知道,排列组合和杨辉三角的关系十分密切,我们可以通过杨辉三角得到一些组合数。
如上面的图,我们发现右边就是一个杨辉三角,杨辉三角的性质大家应该都知道吧,出了两端的数字为1,其他的就是当前位置的数为上面两边的数的和。如果右边那个图不是很清楚,那就看一下左边的图,也就是这个位置的数为上面的位置的数加上上面位置左边的数。转化为一个DP方程就是.这个公式学过排列组合的同学应该都知道吧。
- 代码如下
- 需要进行递归处理,时间复杂度为O(n*m)
- 每层递归的空间复杂度为O(1),最坏的情况是递归n层,所以空间复杂度为O(n)
class Solution {
public:
/**
*
* @param m int整型
* @param n int整型
* @return int整型
*/
// 计算C(a,b)
int C(int a,int b){
// b大于a的情况不存在,所以返回0
if(b>a) return 0;
if(b==0||b==a) return 1; // 如果b=0,或者b=a,那么只有一种选法
return C(a-1,b)+C(a-1,b-1); // 否则进行递归
}
int uniquePaths(int m, int n) {
// write code here
return C(n+m-2,n-1);
}
};解法三 非递归DP写法
这种写法和第二种的思路是一样的,但是这里采用的是非递归的写法。需要我们存储之前计算过的每一种状态,当前的状态由之前的状态转移过来。
代码如下
- 双重循环遍历,时间复杂度为O(nm)
- 开了一个二维数组存储中间的每一个状态,空间复杂度为O(nm)
class Solution {
public:
/**
*
* @param m int整型
* @param n int整型
* @return int整型
*/
// 定义状态方程,表示在杨辉三角里面的第i行第j个位置
int dp[220][220];
int uniquePaths(int m, int n) {
// write code here
for(int i=0;i<=205;i++){
for(int j=0;j<=i;j++){
// 状态转移,当C(i,0)或者C(i,i)很显然就是1
if(j==0||j==i) dp[i][j]=1;
// 否则进行状态的转移,转移方程就是下面的公式
else dp[i][j]=dp[i-1][j]+dp[i-1][j-1];
}
}
return dp[n+m-2][n-1];
}
};
京公网安备 11010502036488号