网格中的路径规划问题(含python代码)

背景:还是秋招刷题
参考:https://www.bilibili.com/video/av57064859?from=search&seid=769707923257914088 (B站果然是学习的网站)


题目:一个M*N网格,从左下角(0,0)出发,走到右上角。只能向上走和向右走,有多少条路径?


一、无障碍物版本

解题思路:
不管怎样,都要往上走M-1次,往右走N-1次,且一共一定要走N+M-2次
也就是总共选择机会有N+M-2次,其中选M-1次往上走,剩下的是往右走,即C(N+M-2,M-1),当然也可以C(N+M-2,N-1)。

排列组合公式:
排列A(m,n)=m×(m-1).(m-n+1)=m!/(m-n)!(m为下标,n为上标) 从m个中取n个的所有排列
组合C(m,n)=P(m,n)/P(n,n) =m!/n!(m-n)!;从m个取n个,不用排列

代码如下:

def barrier_free_all_ways(M,N):
    f=lambda n:1 if n==0 else n*f(n-1)  #n是形参,如果n=0则返回1,否则返回n*f(n-1)  #这一句是求阶乘,复杂度为O(n)
    C=lambda p,q:f(p)/f(q)/f(p-q) #这一句是C的公式
    return C(M+N-2,M-1)   
#print(barrier_free_all_ways(3,3))

二、有一个障碍物版本

假设障碍物座标为(x,y)

解题思路:
有效走法=所有走法-经过障碍物的走法
=C(M+N-2,M-1)-把(x,y)看作终点任走 * (x,y)到(M-1,N-1)任走
=C(M+N-2,M-1)-C(x+y,x)*C(M+N-x-y-2,M-x-1)

代码如下:

def barrier_one_all_ways(M,N,x,y):
    f=lambda n:1 if n==0 else n*f(n-1)
    C=lambda p,q:f(p)/f(q)/f(p-q)
    return C(M+N-2,M-1)-C(x+y,x)*C(M+N-x-y-2,M-x-1)  #最终复杂度为O(max{m,n})或O(m+n)  #可不可以写成O(max{m,n},m+n})???
#print(barrier_one_all_ways(3,3,1,1))

三、有多个障碍物版本

假设障碍物座标为一个元组构成的列表list[(x1,y1),(x1,y1)…(xn,yn)]

思路一:暴力递归
从起点开始,递归计算下一步往上和往右走到终点分别有多少种方案,加起来。
特殊情况:
1)当前点是障碍物,返回0种方案
2)当前点是终点,返回1种方案
3)递归中走到了上边框或右边框且不是终点,那就分别只能继续往右走和往上走

代码如下:

def barrier_many_all_ways_rec(M,N,m,n,list): #m,n是当前点
    if (m,n) in list:return 0
    if m==M-1 and n==N-1:return 1
    if m==M-1 and not n==N-1:return barrier_many_all_ways_rec(M,N,m,n+1,list)
    if not m==M-1 and n==N-1:return barrier_many_all_ways_rec(M,N,m+1,n,list)
    return barrier_many_all_ways_rec(M,N,m,n+1,list)+barrier_many_all_ways_rec(M,N,m+1,n,list)
print(barrier_many_all_ways_rec(9,9,0,0,[(1,1),(3,5),(6,8)]))

优化:也可以把起点和终点换一下,即从右上角到左下角,这样可以少两个变量


思路二:动态规划
建一个全为0二维数组来存放从起点到当前点有多少种方案。遍历这个二维数组,起点设为1。遍历到终点则结束。
每次遍历中判断,如果当前点是障碍物,那么数组中的值为0;如果当前点不是障碍物,则数组值为其左点的方案数+其下点的方案数之和
特殊情况:
如果当前点在下边框或左边框上,那么分别只加其左边的和其下边的

代码如下:

def barrier_many_all_ways_dp(M,N,list):
    arr = [[0] * N] * M
    arr[0][0]=1
    for m in range(len(arr)):
        for n in range(len(arr[0])):
            if (m,n) in list:
                arr[m][n]=0
            elif m!=0 and n==0:
                arr[m][n] = arr[m-1][n]
            elif m==0 and n!=0:
                arr[m][n] = arr[m][n-1]
            else:
                arr[m][n]=arr[m][n-1]+arr[m-1][n]
    return arr[M-1][N-1]  #其实可以输出任意位置的方案数
print(barrier_many_all_ways_dp(9,9,[(1,1),(3,5),(6,8)]))

优化:整个矩阵初始化为1,障碍处置0


其他思路:排列组合+递归

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章