【算法】動態規劃 矩陣 :62. 不同路徑

62. 不同路徑

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

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

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

在這里插入圖片描述
在這里插入圖片描述

實現

下面分別給出基于動態規劃的空間優化寫法——C++ 與 Python 兩種實現,均為 O ( m n ) O(mn) O(mn) 時間、 O ( n ) O(n) O(n) 空間:

#include <bits/stdc++.h>
using namespace std;// 計算從 (0,0) 到 (m-1,n-1) 的不同路徑數
int uniquePaths(int m, int n) {// dp[j] 表示當前行到達列 j 的路徑數vector<int> dp(n, 1);// 第一行所有 dp[j] 初始化為 1(只能一直向右走)for (int i = 1; i < m; ++i) {// 第 0 列始終為 1(只能一直向下走)for (int j = 1; j < n; ++j) {// 來自上方 (上一行同列) + 來自左方 (當前行前一列)dp[j] += dp[j - 1];}}return dp[n - 1];
}int main() {int m, n;// 讀入 m, ncin >> m >> n;cout << uniquePaths(m, n) << "\n";return 0;
}
def unique_paths(m: int, n: int) -> int:"""dp[j] 表示當前行到達列 j 的路徑數。初始 dp[:] = [1,1,...,1],代表第 0 行只能一直向右。"""dp = [1] * nfor _ in range(1, m):# dp[0] 保持為 1for j in range(1, n):dp[j] += dp[j - 1]return dp[-1]if __name__ == "__main__":# 輸入格式:m nm, n = map(int, input().split())print(unique_paths(m, n))

思路回顧:

  1. 定義狀態:用一維數組 dp[j] 表示「到達當前行第 j j j 列的路徑數」。

  2. 邊界條件:第一行所有 dp[j]=1(只能向右),第一列隱式保持 dp[0]=1(只能向下)。

  3. 狀態轉移:對于第 i i i 行的第 j j j 列,有

    d p [ j ] ← d p [ j ] ( 上一行同列 ) + d p [ j ? 1 ] ( 當前行前一列 ) . dp[j] \leftarrow dp[j]\;(\text{上一行同列}) \;+\; dp[j-1]\;(\text{當前行前一列})\,. dp[j]dp[j](上一行同列)+dp[j?1](當前行前一列).

  4. 結果保存在 dp[n-1]dp[-1]

這種「滾動數組」的做法將空間復雜度從 O ( m n ) O(mn) O(mn) 降至 O ( n ) O(n) O(n)




通俗解釋

下面我們用最直觀的“走格子填數字”的方式來理解這道題的動態規劃解法。


一、問題回顧

  • 機器人在一個 m × n m\times n m×n 的網格里,只能「向右」或「向下」走一步,問從左上角到右下角一共有多少條不同的路徑。

二、最通俗的思路:給每個格子“打分”

  1. 把網格想象成一個表格,我們給每個格子 ( i , j ) (i,j) (i,j) 一個數 dp[i][j],它代表「到達 ( i , j ) (i,j) (i,j) 的不同走法數」。

  2. 為什么格子 ( i , j ) (i,j) (i,j) 的分數==它上面格子 + 它左邊格子的分數?

    • 因為機器人最后一步,要么從上面 ( i ? 1 , j ) (i-1,j) (i?1,j) 下來,要么從左邊 ( i , j ? 1 ) (i,j-1) (i,j?1) 右來。

    • 所以,

      d p [ i ] [ j ] = d p [ i ? 1 ] [ j ] + d p [ i ] [ j ? 1 ] . dp[i][j] = dp[i-1][j] + dp[i][j-1]. dp[i][j]=dp[i?1][j]+dp[i][j?1].

  3. 邊界條件:第一行和第一列只能走一條路線

    • 第一行 ( 0 , 0 ) → ( 0 , 1 ) → ? (0,0)\to(0,1)\to\cdots (0,0)(0,1)? 全是「一直向右」,所以它們 dp[0][*] = 1
    • 第一列 ( 0 , 0 ) → ( 1 , 0 ) → ? (0,0)\to(1,0)\to\cdots (0,0)(1,0)? 全是「一直向下」,所以它們 dp[*][0] = 1

三、舉例:3×3 網格

我們把 dp 表格畫出來(行列都從 0 開始):

i\j012
0111
11
21
  • 第一行、第一列先填 1。

接下來按行、按列填:

  1. ( 1 , 1 ) (1,1) (1,1):上面是 ( 0 , 1 ) = 1 (0,1)=1 (0,1)=1,左邊是 ( 1 , 0 ) = 1 (1,0)=1 (1,0)=11+1=2
  2. ( 1 , 2 ) (1,2) (1,2):上面是 ( 0 , 2 ) = 1 (0,2)=1 (0,2)=1,左邊是 ( 1 , 1 ) = 2 (1,1)=2 (1,1)=21+2=3
  3. ( 2 , 1 ) (2,1) (2,1):上面是 ( 1 , 1 ) = 2 (1,1)=2 (1,1)=2,左邊是 ( 2 , 0 ) = 1 (2,0)=1 (2,0)=12+1=3
  4. ( 2 , 2 ) (2,2) (2,2):上面是 ( 1 , 2 ) = 3 (1,2)=3 (1,2)=3,左邊是 ( 2 , 1 ) = 3 (2,1)=3 (2,1)=33+3=6

最終表格:

i\j012
0111
1123
2136

右下角 ( 2 , 2 ) (2,2) (2,2) 就是 6,也就是 3×3 網格的答案。


四、滾動數組優化到只用一維

其實我們只需要「上一行」和「當前行」信息,就能把二維壓成一維。

  • 用一個長度為 n n n 的數組 dp[j] 表示「當前行到第 j j j 列的路徑數」。

  • 每次更新時:

    dp[j] = dp[j](上面的舊值) + dp[j-1](左邊剛更新的值)
    
  • 第一行先把 dp[:] = [1,1,1,...] 初始化好。


五、代碼實現

1. C++ 版( O ( n ) O(n) O(n) 空間)

#include <bits/stdc++.h>
using namespace std;int uniquePaths(int m, int n) {vector<int> dp(n, 1);  // 第一行:到每列只有 1 種走法for (int i = 1; i < m; ++i) {       // 從第 2 行開始for (int j = 1; j < n; ++j) {   // 第一列 dp[0] 始終是 1dp[j] += dp[j - 1];         // 上 + 左}}return dp[n - 1];
}int main() {int m, n;cin >> m >> n;cout << uniquePaths(m, n) << "\n";return 0;
}

2. Python 版( O ( n ) O(n) O(n) 空間)

def unique_paths(m: int, n: int) -> int:dp = [1] * n           # 第一行:全 1for _ in range(1, m):  # 從第 2 行開始for j in range(1, n):dp[j] += dp[j - 1]  # 上 + 左return dp[-1]if __name__ == "__main__":m, n = map(int, input().split())print(unique_paths(m, n))

六、結題思路小結

  1. 狀態定義dp[i][j](或滾動數組里的 dp[j])表示「到達格子 ( i , j ) (i,j) (i,j) 的路徑數」。
  2. 狀態轉移dp[i][j] = dp[i-1][j] + dp[i][j-1] —— 最后一部要么從上面下來,要么從左邊來。
  3. 邊界:第一行/第一列只能一直往右/往下,路徑數都是 1。
  4. 空間優化:因為只用到「上一行」信息,可以把二維壓成一維,dp[j] = dp[j] + dp[j-1]

這樣邏輯就很清晰:

  • 想象你在走格子,每個格子都“接收”來自上方和左方的走法數;
  • 一圈圈填下去,最后在終點就能看到總路數。

希望這樣的流程化、填表式講解能幫助你徹底搞懂!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/89337.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/89337.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/89337.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

LabVIEW調用Excel宏實現數據可視化

通過LabVIEW 的 ActiveX 接口&#xff0c;調用 Excel 應用程序&#xff0c;實現打開指定Excel 工作簿并運行其中宏&#xff08;如 “GraphData” 宏&#xff09;&#xff0c;將工作表數據以圖表形式展示。通過 ActiveX 自動化技術&#xff0c;打通 LabVIEW 與 Excel 交互通道&a…

初始CNN(卷積神經網絡)

卷積神經網絡&#xff08;Convolutional Neural Network&#xff0c;簡稱 CNN&#xff09;作為深度學習的重要分支&#xff0c;在圖像識別、目標檢測、語義分割等領域大放異彩。無論是手機上的人臉識別解鎖&#xff0c;還是自動駕駛汽車對道路和行人的識別&#xff0c;背后都離…

深度解析Spring Bean生命周期:從字節碼到可用對象的奇幻旅程

&#x1f331; 深度解析Spring Bean生命周期&#xff1a;從字節碼到可用對象的奇幻旅程 你是否曾困惑&#xff1a;為什么PostConstruct有時不執行&#xff1f;為什么循環依賴報錯如此難解&#xff1f;為什么AOP代理在某些場景失效&#xff1f; 本文將徹底拆解Spring Bean的16個…

MySQL 復合查詢和內外連接 -- 子查詢,多表查詢,自連接,合并查詢,表的內外連接

目錄 1. 子查詢 1.1 單行子查詢 1.2 多行子查詢 1.3 多列子查詢 1.4 在 from 子句中使用子查詢 2. 多表查詢 3. 自連接 4. 合并查詢 4.1 union 4.2 union all 5. 表的內連接 6. 表的外連接 下列先給出該博客中所用到的所有表的數據。 &#xff08;1&#xff09;部…

【STM32+LAN9252+HAL庫】EtherCAT從站搭建 保姆級教程

目錄 一、生成協議棧及XML文件 二、使用stm32CuboMX配置外設 三、協議棧移植 鑒于本人對EtherCAT的掌握程度十分有限&#xff0c;這篇文章僅作為我搭建基礎從站的過程記錄不做更多講解。本文內容主要為SPI模式的基礎搭建&#xff0c;更多深入的學習資料和細節&#xff0c;大家…

【LeetCode 熱題 100】239. 滑動窗口最大值——(解法二)滑動窗口+單調隊列

Problem: 239. 滑動窗口最大值 題目&#xff1a;給你一個整數數組 nums&#xff0c;有一個大小為 k 的滑動窗口從數組的最左側移動到數組的最右側。你只可以看到在滑動窗口內的 k 個數字。滑動窗口每次只向右移動一位。返回滑動窗口中的最大值 。 【LeetCode 熱題 100】239. 滑…

MySQL 8.0 連接 5.x 服務器認證問題

總的來說&#xff0c;答案是&#xff1a;可以&#xff0c;但是需要特別注意認證方式的兼容性問題。 MySQL 8.0 引入了新的默認認證插件 caching_sha2_password&#xff0c;而 MySQL 5.x&#xff08;及更早版本&#xff09;使用的是 mysql_native_password。當你用一個 8.0 的客…

Spring原理揭秘(一)

什么是spring&#xff1f; spring框架是一個輕量級的開源的JavaEE框架。 所謂輕量級則是&#xff1a;占用空間小&#xff0c;代碼侵入性低&#xff0c;代碼耦合度低&#xff0c;降低代碼復雜度&#xff0c;可以輕易適配多種框架。 隨著spring的不斷發展&#xff0c;它所占用…

Visual Studio Code自用搜索技巧整理

多文件跨行搜索 用途 在多個日志文件中搜索跨行日志 方法 1.用VS Code打開待搜索文件所在的目錄&#xff1b; 2.按快捷鍵&#xff08;CtrlShiftF&#xff09;打開全局搜索&#xff1b; 3.點擊搜索框右側的開啟正則表達式&#xff1b; 4.輸入正則表達式&#xff0c;例如&…

Axure PR 9 驗證碼登錄 案例

大家好&#xff0c;我是大明同學。 這期內容&#xff0c;我們來用Axure來制作一個短信驗證登錄頁面的小案例。 驗證碼登錄小案例 創建手機號輸入框所需的元件 1.打開一個新的 RP 文件并在畫布上打開 Page 1。 2.在元件庫中拖出一個矩形元件&#xff0c;選中矩形元件&#xf…

監聽器模式

1. 問題背景 假設我們有一個 銀行賬戶管理系統&#xff0c;該系統需要監控用戶賬戶余額的變動&#xff0c;并在發生變動時&#xff0c;自動執行一些相關的操作&#xff0c;比如發送 余額變動通知&#xff08;如短信、郵件等&#xff09;。為了實現這一功能&#xff0c;我們希望…

帕魯杯應急響應賽題:知攻善防實驗室

一、背景信息 在這個跳躍的數字舞臺上&#xff0c;數據安全成了政企單位穩航的重要壓艙石。某政企單位&#xff0c;作為一艘駛向未來 的巨輪&#xff0c;對數據的把控絲毫不敢松懈。眼下&#xff0c;我們即將啟航一場無與倫比的探險——“信息安全探索之 旅”。 這趟旅程的目的…

【硬核數學】2.2 深度學習的“微積分引擎”:自動微分與反向傳播《從零構建機器學習、深度學習到LLM的數學認知》

歡迎來到本系列的第七篇文章。在上一章&#xff0c;我們用張量武裝了我們的線性代數知識&#xff0c;學會了如何描述和操作神經網絡中的高維數據流。我們知道&#xff0c;一個神經網絡的“前向傳播”過程&#xff0c;就是輸入張量經過一系列復雜的張量運算&#xff08;矩陣乘法…

DAY 45 Tensorboard使用介紹

浙大疏錦行https://blog.csdn.net/weixin_45655710知識點回顧&#xff1a; tensorboard的發展歷史和原理tensorboard的常見操作tensorboard在cifar上的實戰&#xff1a;MLP和CNN模型 作業&#xff1a;對resnet18在cifar10上采用微調策略下&#xff0c;用tensorboard監控訓練過程…

2023年全國碩士研究生招生考試英語(一)試題總結

文章目錄 題型與分值分布完形填空錯誤 1&#xff1a;考察連詞 or 前后內容之間的邏輯關系錯誤2&#xff1a;錯誤3&#xff1a;錯誤4&#xff1a;這個錯得最有價值&#xff0c;因為壓根沒讀懂錯誤5&#xff1a;學到的短語&#xff1a; 仔細閱讀排序/新題型翻譯小作文大作文 題型…

react-數據Mock實現——json-server

什么是mock&#xff1f; 在前后端分離的開發模式下&#xff0c;前端可以在沒有實際后端接口的支持下先進行接口數據的模擬&#xff0c;進行正常的業務功能開發 json-server實現數據Mock json-server是一個node的包&#xff0c;可以在不到30秒內獲得零編碼的完整Mock服務 實現…

使用POI導入解析excel文件

首先校驗 /*** 校驗導入文件* param file 上傳的文件* return 校驗結果&#xff0c;成功返回包含成功狀態的AjaxResult&#xff0c;失敗返回包含錯誤信息的AjaxResult*/private AjaxResult validateImportFile(MultipartFile file) {if (file.isEmpty()) {return AjaxResult.er…

從0開始學習計算機視覺--Day06--反向傳播算法

盡管解析梯度可以讓我們省去巨大的計算量&#xff0c;但如果函數比較復雜&#xff0c;對這個損失函數進行微分計算會變得很困難。我們通常會用反向傳播技術來遞歸地調用鏈式法則來計算向量每一個方向上的梯度。具體來說&#xff0c;我們將整個計算過程的輸入與輸入具體化&#…

企業流程知識:《學習觀察:通過價值流圖創造價值、消除浪費》讀書筆記

《學習觀察&#xff1a;通過價值流圖創造價值、消除浪費》讀書筆記 作者&#xff1a;邁克魯斯&#xff08;Mike Rother&#xff09;&#xff0c;約翰舒克&#xff08;John Shook&#xff09; 出版時間&#xff1a;1999年 歷史地位&#xff1a;精益生產可視化工具的黃金標準&am…

Day02_C語言IO進程線程

01.思維導圖 02.將當前的時間寫入到time. txt的文件中&#xff0c;如果ctrlc退出之后&#xff0c;在再次執行支持斷點續寫 1.2022-04-26 19:10:20 2.2022-04-26 19:10:21 3.2022-04-26 19:10:22 //按下ctrlc停止&#xff0c;再次執行程序 4.2022-04-26 20:00:00 5.2022-04-26 2…