题解 | #走方格的方案数#

走方格的方案数

http://www.nowcoder.com/practice/e2a22f0305eb4f2f9846e7d644dba09b

题目的主要信息:

  • 一个nmn*m的表格,从左上角走到右下角的方法种数
  • 每次只能走下或者右
  • 不能回头

方法一:递归

具体做法:

容易想到的是,在第一步时可以选择向右或者向下,只需要当前的路径选择上加上(n,m1)(n,m-1)(n1,m)(n-1,m)的矩阵路径选择即可。而(n,m1)(n,m-1)(n1,m)(n-1,m)又是(n,m)(n,m)的子问题,可以继续递归下去。只需要用iijj表示当前位置,后续限制边界的向下或者向右即可。这是容易想到的方法, 下图为从(00)(0,0)开始的每个子问题的递归方向,每次可以选择向下或者是向右递归,以缩短为子问题:

alt

#include<iostream>
#include<vector>
using namespace std;

int recursion(int i, int j, int m, int n){ //递归计算方案数
    if(n == i || m == j) //到边界只有一种走法
        return 1;
    return recursion(i + 1, j, m, n) + recursion(i, j + 1, m, n); //进入子问题
}
int main(){
    int m, n;
    while(cin >> n >> m){
        cout << recursion(0, 0, m, n) << endl;
    }
    return 0;
}

复杂度分析:

  • 时间复杂度:O(2n)O(2^n),其中n=max(m,n)n=max(m,n),递归是一个树型递归
  • 空间复杂度:O(n)O(n),递归栈最大深度为树的深度nn

方法二:动态规划

具体做法:

根据上述方法一,我们用dp[i][j]dp[i][j]表示到第i行j列为止的方案数,它等于到它左边的方案数加上到它上边的方案数的总和,如果是首行或者首列就等于它旁边的方案数,没有比的选择,如果是第一个就只有1种。

#include<iostream>
#include<vector>
using namespace std;

int main(){
    int m, n;
    while(cin >> n >> m){
        vector<vector<int> > dp(n + 1, vector<int>(m + 1, 0)); //dp[i][j]表示到第i行j列为止的方案数
        for(int i = 0; i <= n; i++)
            for(int j = 0; j <= m; j++){
                if(i == 0 && j == 0) //最开始,一种方法
                    dp[i][j] = 1;
                else if(i == 0) //行数到顶,等于旁边列的方法
                    dp[i][j] = dp[i][j - 1];
                else if(j == 0) //列数到左边,等于旁边行的方法
                    dp[i][j] = dp[i - 1][j];
                else //等于左边加上边的方法
                    dp[i][j] = dp[i][j - 1] + dp[i - 1][j];
            }
        cout << dp[n][m] << endl;
    }
    return 0;
}

复杂度分析:

  • 时间复杂度:O(nm)O(nm),两层遍历循环
  • 空间复杂度:O(nm)O(nm),dp数组的空间为mnmn

方法三:组合数学

具体做法:

不管怎么走,总要从左上到右下,那么要向下走nn次,向右走mm次,总共也只走m+nm+n次,那么方法数就是从这m+nm+n次中选出mm次向右走的方案,即Cm+nmC^m_{m+n},那么最终方案数就是:

(m+1)(m+2)(m+3)....(m+n)n!\frac{(m+1)(m+2)(m+3)....(m+n)}{n!}

我们只需要遍历一个nn计算分子分母即可计算总方案数。

#include<iostream>
using namespace std;

int main(){
    int m, n;
    while(cin >> n >> m){
        int x = 1;
        int y = 1;
        for(int i = 1; i <= n; i++){
            x *= m + i; //获取分子
            y *= i; //获取分母
        }
        cout << x / y << endl;
    }
    return 0;
}

复杂度分析:

  • 时间复杂度:O(n)O(n),遍历一个nn
  • 空间复杂度:O(1)O(1),无额外空间
孤帆远影碧空尽 文章被收录于专栏

牛客网各类题单题解~

全部评论
这个不是C(m , m+n) 是 A(m , m+n) 不是组合是排列,。。。。 你用组合的式子是错的
1 回复 分享
发布于 06-28 13:21 浙江
动态规划那里,只需要i 或者j 一个为0,结果就是1,不需要重复判断
1 回复 分享
发布于 2023-12-20 20:34 江西
这确实是从 m + n 个元素中选出 n 次向右走的方案,即 C(m+n,n) 的表达式
1 回复 分享
发布于 2023-08-27 13:27 湖北
厉害
1 回复 分享
发布于 2022-09-20 09:21 陕西
排列组合这个没看懂,为什么分子分母是这么算?
1 回复 分享
发布于 2022-07-20 16:27
排列组合解法补充: 举个例子:如果向右走2次,向下走2次,则总共走了4次 具体的走法应该是:下下右右,下右右下,右右下下,下右下右,右下右下,右下下右(总共6种) 可以理解为从4个位置上选取2个位置来放“右”,则使用那个组合数(评论中打不出来,就不打了) 不过感觉答主这边组合数是不是写错了,m+n个步骤中选取m个“右”得到的分式中分母应该是n!,分子应该是(n+1)(n+2)...(n+m)
点赞 回复 分享
发布于 2022-07-21 15:55

相关推荐

评论
93
17
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务