计算二维数组上的路径数(网格旅行者)

Posted

技术标签:

【中文标题】计算二维数组上的路径数(网格旅行者)【英文标题】:Count number of paths on 2d-array (grid Traveller) 【发布时间】:2021-08-05 06:45:58 【问题描述】:

我有以下目标:“给定二维 m x n 矩阵,编写一个算法来计算从左上角到右下角的所有可能路径。您只能在两个方向上移动,向右移动或移动下。” 这个问题很容易分解。例如,一个 3 x 3 网格具有与 2 x 3 网格和 3 x 2 网格组合一样多的路径。所以递归解决方案非常直观。

我已经实现了一个递归解决方案和一个带有记忆的解决方案(二维数组存储已经计算的路径)。在 C 中,但不知何故,当网格变得太大时,两个函数仍然返回相同的,但负解(即 15 x 15 工作正常,18 x 18,不再)。知道这是从哪里来的吗?我的代码附在下面。 另外..有没有一种很好的方法来编码记忆解决方案的数组?如果我使用A[m][n] 作为函数参数,它就不能正常工作,所以我暂时只对其进行硬编码。

谢谢!

#include <stdio.h>

int gridTravel(int m, int n)
    if (m == 0 || n == 0)
        return 0;
    
    if (m == 1 || n == 1)
        return 1;
    
    return gridTravel(m-1, n) + gridTravel(m, n-1);


int gridTravelMemo(int m, int n, int A[15][15])
    if (m == 0 || n == 0)
        return 0;
    
    if (m == 1 || n == 1)
        return 1;
    
    if (A[m-1-1][n-1] == 0)
        A[m-1-1][n-1] = gridTravelMemo(m-1, n, A);
    
    if (A[m-1][n-1-1] == 0)
        A[m-1][n-1-1] = gridTravelMemo(m, n-1, A);
    
    return  A[m-1-1][n-1] + A[m-1][n-1-1];


int main()
    int res = gridTravel(15,15);
    printf("There is a total of %d ways to traverse the grid (Grid size is 15 by 15).\n", res);

    int res2 = gridTravel(18,18);
    printf("There is a total of %d ways to traverse the grid (Grid size is 18 by 18).\n", res2);
    
    int A[15][15] = 0;
    int res_memo = gridTravelMemo(15,15,A);
    printf("There is a total of %d ways to traverse the grid (Grid size is 15 by 15).\n", res_memo);

    return 0;


【问题讨论】:

【参考方案1】:

我不会在这里使用任何动态编程或递归,而是使用组合数学来解决它。

正如@Eric Postpischil 所指出的,在计算路径数时,您应该使用更广泛的类型,例如uint64_tunsigned long long 或类似的类型。

说明

您被问到的是长度为m - 1 + n - 1 的路径如何从左上角开始并在右下角结束。为什么是m + n - 2

好吧,因为您只能向右或向下移动。最初,您位于目标的m - 1 行和n - 1 列之外。每一步,您都会使1 行或1 列更接近目标。因此,您需要准确地采取m + n - 2 步骤。

但是有多少种组合呢?在m + n - 2 步中,您必须准确地向下走m - 1 步和向右走n - 1 步。因此,从m + n - 2 步中取出m - 1 垂直步(或n - 1 水平步)的方法数为 Cm-1m+n-2 或 Cn-1m+n-2 (如果需要,请在此处查看Binomial coefficient 的定义)。

公式

以下两个公式产生相同的结果

Cm-1m+n-2

Cn-1m+n-2

代码

然后,您的方法可以重新实现如下。请注意,如果nm 变得相对较大,您可能会遇到溢出。此外,我对二项式系数的实现并不是最佳的。

#define MIN(a,b) (((a)<(b))?(a):(b))

uint64_t gridTravel(uint64_t m, uint64_t n) 
    if (m == n && m == 1) 
        return 0;
    

    uint64_t result = 1;
    for (uint64_t i = 1; i <= MIN(m - 1,n - 1); i++) 
        result *= (m + n - 1 - i);
        result /= i;
    

    return result;

【讨论】:

【参考方案2】:

对 res 和 res1 使用 long 变量类型来代替 int 类型。

递归发现超出了 int 限制。

【讨论】:

以上是关于计算二维数组上的路径数(网格旅行者)的主要内容,如果未能解决你的问题,请参考以下文章

将坐标线写入表示网格的二维数组

从二维网格上的碰撞数据构建距离图

计算二维数组的页面错误数

LeetCode 1260 二维网格迁移[数组] HERODING的LeetCode之路

LeetCode动态规划#02图解不同路径I + II(首次涉及二维dp数组,)

算法第3章上机实践报告