题目意思

  • 给你一个二维矩阵,需要找出从左上角走到右下角的不同的方案数。每次只能向下或者向右移动。过程中只要有一步不一样这种走法就不一样。

    思路分析

  • 题目意思很简单,其实这是一个排列组合的经典的题目。只要用到高中知识。我们发现,每种方案走的步数其实是固定的。对于一个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];
    }
};