<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          九十四、動態(tài)規(guī)劃系列之路徑問題

          共 10400字,需瀏覽 21分鐘

           ·

          2021-03-20 16:52


          「@Author:Runsen」

          在動態(tài)規(guī)劃最短路徑經(jīng)常提及,在上幾篇介紹過相關(guān)的最短路徑的問題,介紹過使用Dijkstra算法去求解,但是Dijkstra算法是基于貪心算法,按路徑長度遞增的次序一步一步并入來求取,算法效率低效。

          Dijkstra算法原理是:如果從圖中某一頂點(源點)到達另一頂點(終點)的路徑可能不止一條,如何找到一條路徑使得沿此路徑上各邊的權(quán)值總和(稱為路徑長度)達到最小。動態(tài)規(guī)劃可以說算法中最優(yōu)秀的算法,因為在此介紹動態(tài)規(guī)劃系列中的路徑問題。

          下面是對應(yīng)的動態(tài)規(guī)劃解決的路徑問題總結(jié):

          • 62. 不同路徑
          • 63. 不同路徑 II
          • 64. 最小路徑和
          • 120. 三角形最小路徑和

          62.不同路徑

          一個機器人位于一個 m x n 網(wǎng)格的左上角 (起始點在下圖中標記為 “Start” )。

          機器人每次只能向下或者向右移動一步。機器人試圖達到網(wǎng)格的右下角(在下圖中標記為 “Finish” )。

          問總共有多少條不同的路徑?

          下面是測試用例:

          輸入:m = 3, n = 7
          輸出:28

          在此,我們可以完全當作一道初中數(shù)學(xué)題來對待,既然最后要到達終點,那肯定需要往下走n-1步,往右走m-1步,總共需要走n+m-2步。

          還有無論往右走還是往下走他的總的步數(shù)是不會變的,往右走的步數(shù)一定等于n,往下走的步數(shù)一定等于m

          因此,可以換一種說法相當于總共要走n+m-2步,往右走m-1步,總共有多少種走法,很明顯這就是一個高中數(shù)學(xué)的排列組合問題,公式如下

          排列組合的計算公式:

          代入就可以得到:

          # factorial求階乘,也可以自己定義一個
          import math 
          def uniquePaths(self, m: int, n: int) -> int:
                  return int(math.factorial(m+n-2)/math.factorial(m-1)/math.factorial(n-1))

          上面的方法沒有開數(shù)組空間,無論是時間還是空間,都是最優(yōu)的做法。

          對于動態(tài)規(guī)劃的方法,也非常容易理解,定義一個二維數(shù)組dp,來存儲每一個格子的最短路徑數(shù)之和,設(shè) dp 為大小 矩陣,其中 dp[i][j] 的值代表直到走到 (i,j) 的最小路徑和。

          由于只能向右向下移動,所以對于除了邊界外的格子之外的其他格子而言,到達它的方式只能有它上邊的格子下移或者左邊的格子右移,所以到達它的路徑數(shù)為它上邊格子的路徑數(shù)與左邊格子的路徑數(shù)之和,可得狀態(tài)轉(zhuǎn)移方程:dp[i][j] = dp[i-1][j] + dp[i][j-1]

          下圖是二維數(shù)組動態(tài)規(guī)劃求解的過程:

          dp初始化,對于第一行 dp[0][j],或者第一列 dp[i][0],由于都是在邊界,所以只能為 1。

          class Solution:
              def uniquePaths(self, m: int, n: int) -> int:
                  dp = [[0] * n for _ in range(m)]

                  # 初始化
                  for i in range(m):
                      dp[i][0] = 1
                  
                  for j in range(n):
                      dp[0][j] = 1

                  # 根據(jù)轉(zhuǎn)移方程進行遞推
                  for i in range(1, m):
                      for j in range(1, n):
                          dp[i][j] = dp[i-1][j] + dp[i][j-1]
                  
                  return dp[-1][-1]

          當然,你也可以對二維dp數(shù)組壓縮成一維數(shù)組,來代表每一行或者每一列的路徑數(shù)之和,可以對空間進一步的優(yōu)化。

          63. 不同路徑 II

          此題和第一題的添加了一個障礙物,那么從左上角到右下角將會有多少條不同的路徑?

          網(wǎng)格中的障礙物和空位置分別用 1 和 0 來表示。

          測試用例:

          輸入:obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
          輸出:2
          解釋:
          3x3 網(wǎng)格的正中間有一個障礙物。
          從左上角到右下角一共有 2 條不同的路徑:
          1. 向右 -> 向右 -> 向下 -> 向下
          2. 向下 -> 向下 -> 向右 -> 向右

          假設(shè)自己畫的方框是障礙物,那么直接把二維數(shù)組對應(yīng)的位置變成0即可。

          因此,最終的解決過程:

          =

          在此題中,測試用例存在門口就是一個障礙物的情況,因此需要注意初始化條件。

          class Solution:
              def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
                  m, n = len(obstacleGrid), len(obstacleGrid[0])
                  # 門口是一個障礙物
                  if obstacleGrid[0][0] == 1:
                      return 0
                  dp = [[0 for _ in range(n)] for _ in range(m)]
                  dp[0][0] = 1
                  
                  for i in range(1, m):
                      if obstacleGrid[i][0] == 0:
                          dp[i][0] = dp[i-1][0]
                          
                  for j in range(1, n):
                      if obstacleGrid[0][j] == 0:
                          dp[0][j] = dp[0][j-1]
                  
                  for i in range(1, m):
                      for j in range(1, n):
                          # 判斷是不是障礙物
                          if obstacleGrid[i][j] == 1:
                              dp[i][j] = 0
                          else:
                              dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
                  return dp[-1][-1]

          64. 最小路徑和

          給定一個包含非負整數(shù)的 m x n 網(wǎng)格,請找出一條從左上角到右下角的路徑,使得路徑上的數(shù)字總和為最小。

          測試用例:

          輸入:grid = [[1,3,1],[1,5,1],[4,2,1]]
          輸出:7
          解釋:因為路徑 1→3→1→1→1 的總和最小。

          如果采用貪心的算法,會由于顧及當前的利益,而放棄長遠的利益。因此貪心的思路直接否決。

          在此題的動態(tài)規(guī)劃中,狀態(tài)轉(zhuǎn)移方程仍是:dp[i][j] = dp[i-1][j] + dp[i][j-1]

          初始化dp所有的元素均為0,在網(wǎng)格的第一行和第一列的所有路徑和應(yīng)該都是固定的,因為都是向右或者向下移動。

          而當位置(i,j)處時,需要從判斷從向右和向下的dp的數(shù)值,取出最小值,然后加上這個網(wǎng)格的數(shù)字。

          dp[i][j] = min(dp[i-1][j] +grid[i][j],dp[i][j-1]++grid[i][j])

          from typing import List
          class Solution:
              def minPathSum(self, grid: [[int]]) -> int:
                  for i in range(len(grid)):
                      for j in range(len(grid[0])):
                          if i == j == 0continue
                          elif i == 0:  
                              grid[i][j] = grid[i][j - 1] + grid[i][j]
                          elif j == 0:  
                              grid[i][j] = grid[i - 1][j] + grid[i][j]
                          else: grid[i][j] = min(grid[i - 1][j], grid[i][j - 1]) + grid[i][j]
                  return grid[-1][-1]


          if __name__ == "__main__":
              s=Solution()
              t=[[1,3,1],
                [1,5,1],
                [4,2,1]]
              print(s.minPathSum(t))
          # result
          7

          120. 三角形最小路徑和

          此題三角形最小路徑和變換了輸入的矩陣,給定一個三角形 triangle ,找出自頂向下的最小路徑和。

          測試用例

          輸入:triangle = [[2],[3,4],[6,5,7],[4,1,8,3]]
          輸出:11
          解釋:如下面簡圖所示:
             2
            3 4
           6 5 7
          4 1 8 3

          2
          3 4
          6 5 7    
          4 1 8 3
          自頂向下的最小路徑和為 11(即,2 + 3 + 5 + 1 = 11)。

          此題由于空間在壓縮,因此使用一維動態(tài)歸化數(shù)組,定義dp[i]為到第N層第i個節(jié)。

          要想獲得到達第 m 條邊的最小路徑和,需要先獲得到達第m - 1 條邊的最小路徑和。

          由于動態(tài)規(guī)劃可以采用自底向上的逆推思想,也就是把三角形倒過來看。

          狀態(tài)轉(zhuǎn)移方程:dp[j] = min(dp[j], dp[j + 1]) + triangle[i][j]

          class Solution:
              def minimumTotal(self, triangle: List[List[int]]) -> int:
                  dp = triangle[-1]
                  for i in range(len(triangle)-2,-1,-1):
                      for j in range(i+1):
                          dp[j] = triangle[i][j] + min(dp[j],dp[j+1])
                  return dp[0]

          對于原地自頂向下算法,把到達每一層的每個數(shù)的最小路徑都算出來,判斷三種不同狀態(tài)的,根據(jù)下面的狀態(tài)轉(zhuǎn)移方程,不斷地修改原數(shù)組 。

          自頂向下算法可以直接用triangle本身的變量進行計算,達到減小內(nèi)存開銷的效果。

          class Solution:
              def minimumTotal(self, triangle: List[List[int]]) -> int:
                  if len(triangle)==0
                      return 0
                  #動態(tài)規(guī)劃 dp表示走到當前位置的時候的最小路徑
                  for i in range(1,len(triangle)):
                      for j in range(len(triangle[i])):
                          if j==0#最左邊的情況
                              triangle[i][j]+=triangle[i-1][j]
                          elif j==i: #最右邊的情況
                              triangle[i][j]+=triangle[i-1][j-1]
                          else:
                              triangle[i][j]+=min(triangle[i-1][j-1],triangle[i-1][j])
                  return min(triangle[-1]) #返回最后一層最小的一個



          更多的文章

          點擊下面小程序


          - END -

          瀏覽 73
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  黄色直接观看 | 永久免费 未满看片软件 | 亚洲婷婷丁香五月 | 国产日韩在线电影 | 麻豆av影院日韩影院 |