網格中的路徑規劃問題(含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


其他思路:排列組合+遞歸

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