動態規劃入門:從記憶化搜索到動態規劃

在開始對動態規劃的講解之前,我們需要先對記憶化搜索進行回顧:

什么是記憶化搜索?

在搜索過程中,當搜索樹中存在大量重復的節點時,我們可以通過引入一個"備忘錄"(通常是一個數組或哈希表)來優化計算。這個備忘錄會記錄第一次搜索到某個節點時的計算結果。當后續再次遇到相同的節點時,就可以直接從備忘錄中獲取之前存儲的結果,避免了重復計算的開銷。

例如,在計算斐波那契數列時,fib(5) = fib(4) + fib(3),而fib(4)又需要計算fib(3)+fib(2)。如果不使用記憶化,fib(3)會被重復計算多次。使用記憶化后,每個fib(n)只需計算一次。

遞推改遞歸
在用記憶化搜索解決斐波那契數列問題時,如果我們觀察備忘錄的填寫過程,會發現它是一個從左到右依次填充的過程。具體來說:

  1. 先計算并存儲fib(0)和fib(1)的基礎值
  2. 然后根據這兩個值計算fib(2)
  3. 接著用fib(1)和fib(2)計算fib(3)
  4. 以此類推,每個新值都依賴于前面已計算好的值

這種自底向上的計算方式,實際上就是將遞歸過程改寫成了循環形式的遞推。這種改寫不僅減少了函數調用的開銷,還使得計算過程更加直觀。

什么是動態規劃?

動態規劃(Dynamic Programming)是一種用于解決多階段決策問題的算法思想。它將復雜問題分解為多個相互關聯的子問題(稱為狀態),并通過保存子問題的解來避免重復計算,從而顯著提高算法效率。

動態規劃通常適用于具有以下特征的問題:

  1. 最優子結構:問題的最優解包含其子問題的最優解
  2. 重疊子問題:不同的決策序列會到達相同的子問題

從上述描述可以看出,動態規劃結合了分治法的思想(將問題分解為子問題)和剪枝的思想(通過存儲結果避免重復計算)。典型的應用場景包括:

  • 最短路徑問題(如Floyd算法)
  • 背包問題
  • 最長公共子序列
  • 編輯距離計算

(在這篇文章中,筆者只講解最基礎的動態規劃,典序應用場景的運用將留到后續更新中)

在遞推形式的動態規劃中,我們常用下面的專有名詞來表述,因此,要學好并利用好動態規劃,我們首先要弄清楚每一題中一下的概念:

  1. 狀態表示:指 f 數組(備忘錄)中,每一個格子所代表的含義。其中,這個數組也被稱為dp數組,或者dp表。
  2. 狀態轉移方程:指 f 數組中,每一個格子是如何利用其他格子推導出來的。
  3. 初始化:在填表之前,根據題目中默認條件或問題的默認初始狀態,將 f 數組中若干格子先填上值。
例題輔助理解:

光講概念我相信很多小伙伴不能很好的理解或是看不懂,接下來,筆者將通過兩個例題來幫助理解。

下樓梯:頑皮的小明發現,下樓梯時每步可以走 1 個臺階、2 個臺階或 3 個臺階。現在一共有 N 個臺階,你能幫小明算算有多少種方案嗎?(洛谷P10250下樓梯)

在沒有特殊限制的情況下,下臺階問題可以轉化為上臺階問題來處理。具體來說,相當于小明每次可選擇上1、2 或 3 級臺階。當我們要到達第 i 級臺階時,可能來自 i - 1、i - 2 或 i - 3 級臺階。基于這一思路,可以自然地推導出對應的狀態轉移方程。具體的算法流程如下所示:
算法原理:

  1. 狀態表示:f[i] 表示走到第 i 級臺階有多少種方案
  2. 狀態轉移方程:f[i] = f[i - 1] + f[i - 2] + f[i - 3]
  3. 初始化:f[1] = 1 …根據題意自行填寫
  4. 填表順序:從小到大依次填寫
  5. 最終結果:f[n]

代碼實現:

int main()
{cin >> n;f[1] = 1, f[2] = 2, f[3] = 4;for (int i = 4; i <= n; i++)f[i] = f[i - 1] + f[i - 2] + f[i - 3];cout << f[n] << endl;return 0;
}

空間優化:通過采用滾動數組技術循環利用數組,可以有效降低空間復雜度。空間優化:通過采用滾動數組技術循環利用數組,可以有效降低空間復雜度。

int main()
{cin >> n;f[1] = 1, f[2] = 2, f[3] = 4;for (int i = 4; i <= n; i++)f[i % 4] = f[(i - 1) % 4] + f[(i - 2) % 4] + f[(i - 3) % 4];cout << f[n % 4] << endl;return 0;
}
數字三角形:觀察下面的數字金字塔。寫一個程序來查找從最高點到底部任意處結束的路徑,使路徑經過數字的和最大。每一步可以走到左下方的點也可以到達右下方的點(洛谷P1216數字三角形)。數字三角形

在題目中,要求我們找到一條最大路徑,但在這個問題中,我們不能使用貪心算法(易證,這里不再贅述),此時我們就需要使用動態規劃的方式解決,每一個位置 f[i][j] 都是由位置 f[i-1][j-1] 或 f[i-1][j] 移動到的,因此,我們只需要找到max(f[i - 1][j - 1], f[i - 1][j])再加上 a[i][j] 即可找到從起點到達 f[i][j] 位置的最大路徑。最后,我們只需要遍歷一下 f 數組的最下面一行即可找出最大路徑。
算法原理:

  1. 狀態表示:f[i][j]表示從[1,1]走到[i,j]位置時,所有方案下的最大權值
  2. 狀態轉移方程: f[i][j] = max(f[i - 1][j - 1], f[i - 1][j]) + a[i][j]
  3. 初始化:將所有格子全部初始化為負無窮
  4. 填表順序:僅需保證從上到下即可
  5. 最終結果:最后一行的最大值

代碼實現:

int main()
{cin >> n;memset(f, -0x3f, sizeof f);for (int i = 1; i <= n; i++)for (int j = 1; j <= i; j++)cin >> a[i][j];f[1][1] = a[1][1];for (int i = 2; i <= n; i++)for (int j = 1; j <= i; j++)f[i][j] = max(f[i - 1][j - 1], f[i - 1][j]) + a[i][j];int max = 0;for (int i = 1; i <= n; i++)if (f[n][i] > max)max = f[n][i];cout << max << endl;return 0;
}

空間優化:可以將 f 數組從二維降為一維。由于每次更新時都是從左到右順序處理,且使用過的舊值不會被重復利用,因此可以不斷覆蓋原數組,僅保留一維數組來記錄最終結果。可以將 f 數組從二維降為一維。由于每次更新時都是從左到右順序處理,且使用過的舊值不會被重復利用,因此可以不斷覆蓋原數組,僅保留一維數組來記錄最終結果。

int main()
{cin >> n;memset(f, -0x3f, sizeof f);for (int i = 1; i <= n; i++)for (int j = 1; j <= i; j++)cin >> a[i][j];f[1] = a[1][1];for (int i = 2; i <= n; i++)for (int j = 1; j <= i; j++)f[j] = max(f[j - 1], f[j]) + a[i][j];int max = 0;for (int i = 1; i <= n; i++)if (f[i] > max)max = f[i];cout << max << endl;return 0;
}

謝謝閱讀!更新不易,點個贊再走吧

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

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

相關文章

Boost搜索引擎 網絡庫與前端(4)

文章目錄前言一、引入網絡庫模塊引入cpp-httplibcpp-httplib測試正式編寫http_server二、前端模塊三、項目的可能拓展總結前言 終于到了最后一篇嘍&#xff0c;嘻嘻&#xff01; 一、引入網絡庫模塊 引入cpp-httplib 下載地址如下&#xff0c;我個人不喜歡新版本 ??cpp-http…

Flink反壓問題

背景在使用flink的過程中&#xff0c;多次遇到過反壓&#xff08;backpressure&#xff09;的問題&#xff0c;這通常是因為數據處理的速率超過了數據源或下游系統的處理能力導致。反壓的底層剖析網絡流控一個重要的概念是網絡流控&#xff0c;如上圖&#xff0c;不同的Consume…

Day5-中間件與請求處理

昨天搞定了異步優化&#xff0c;今天來解決一些實際問題。Day4的API雖然性能不錯&#xff0c;但還缺少一些企業級應用必備的功能。 現在的問題 前端無法訪問API&#xff08;跨域問題&#xff09;沒有請求日志&#xff0c;出問題難以排查錯誤信息格式不統一缺少統一的請求處理機…

【LeetCode熱題100道筆記】反轉鏈表

題目描述 給你單鏈表的頭節點 head &#xff0c;請你反轉鏈表&#xff0c;并返回反轉后的鏈表。 示例 1&#xff1a;輸入&#xff1a;head [1,2,3,4,5] 輸出&#xff1a;[5,4,3,2,1] 示例 2&#xff1a;輸入&#xff1a;head [1,2] 輸出&#xff1a;[2,1] 示例 3&#xff1a;…

Oracle:select top 5

在Oracle數據庫中實現SELECT TOP 5功能需采用特定語法&#xff0c;因其原生不支持TOP關鍵字。以下是兩種主流實現方式&#xff1a;?ROWNUM結合子查詢?先通過子查詢排序數據&#xff0c;再在外層用ROWNUM限制行數&#xff1a;SELECT * FROM ( SELECT * FROM 表名 ORDER BY 排序…

Kubernetes(k8s) 增量更新 po

文章目錄前言k8s 增量更新 po1. 導出要新建po 的控制器配置2. 配置詳解3. 重新生效前言 如果您覺得有用的話&#xff0c;記得給博主點個贊&#xff0c;評論&#xff0c;收藏一鍵三連啊&#xff0c;寫作不易啊^ _ ^。 ??而且聽說點贊的人每天的運氣都不會太差&#xff0c;實在…

基于stm32的車輛安全駕駛預警系統

若該文為原創文章&#xff0c;轉載請注明原文出處。一、 項目背景與引言(一) 研究背景及意義道路交通安全是全球性的重大公共安全問題。據統計&#xff0c;絕大多數交通事故源于駕駛員的危險狀態&#xff08;疲勞、分心、健康突發狀況&#xff09;和危險駕駛行為&#xff08;超…

React學習教程,從入門到精通, React 新創建組件語法知識點及案例代碼(11)

React 新創建組件語法知識點及案例代碼 React 是由 Facebook 開發的一個用于構建用戶界面的 JavaScript 庫。隨著 React 的不斷發展&#xff0c;創建組件的方式也在不斷演進。本文將詳細介紹 React 中創建組件的最新語法&#xff0c;包括函數組件&#xff08;Functional Compo…

SQL Server全鏈路安全防護

SQL Server 的安全性是一個多層次、綜合性的體系&#xff0c;旨在保護數據免受未授權訪問、篡改和泄露。其核心安全機制可概括為以下幾個方面&#xff1a;1. 身份驗證&#xff08;Authentication&#xff09; Windows 身份驗證&#xff1a; 使用 Windows 賬戶&#xff08;域/本…

如何利用Web3提升企業競爭力

在這個信息爆炸的時代&#xff0c;Web3技術以其獨特的去中心化、透明性和用戶主權特性&#xff0c;成為企業提升競爭力的新戰場。本文將深入探討企業如何把握Web3的浪潮&#xff0c;實現業務的飛躍。 1. 把握Web3的核心價值 Web3的核心在于去中心化、透明性和用戶主權。這種模式…

HOW - 在瀏覽器下載一個 Excel 表格文件

文章目錄一、技術方案二、前端具體實現代碼分析轉換邏輯注意事項一、技術方案 后臺返回 base64 數據 {code: 0,data: "base64;...", }前端進行數據格式轉化并下載成 Excel 文件 這篇文章主要介紹第二個步驟的實現。 二、前端具體實現 代碼 src/utils/transform…

【Android】Room數據庫的使用

三三要成為安卓糕手 引入 Room是一個抽象層&#xff0c;對SQLite進行了封裝&#xff0c;簡化了SQLite數據庫的操作&#xff0c;讓開發者能以更加對象化的方式進行數據庫操作&#xff1b;Room解決了SQLite操作繁瑣&#xff0c;容易產生錯誤的問題&#xff0c;讓開發者能以更加對…

Next.js 介紹:為什么選擇它來構建你的下一個 Web 應用?

Next.js 介紹&#xff1a;為什么選擇它來構建你的下一個 Web 應用&#xff1f; 作者&#xff1a;碼力無邊你好&#xff0c;歡迎來到我們的 Next.js 專欄&#xff01;在接下來的 30 篇文章中&#xff0c;我們將一起踏上一段從入門到精通的旅程&#xff0c;深入探索這個強大而優雅…

開發環境 之 編輯器、編譯器、IDE梳理

小生第一次學習編程時&#xff0c;懵懵搞不懂編輯器、編譯器、IDE區別&#xff0c;雖然這對前期學習編程語言語法的影響不是很大&#xff0c;但是現在梳理一下&#xff0c;總歸心里踏實些。 一、概念及區別 IDE是前面幾者的集成&#xff0c;前面幾個分別是IDE的子集。對比維度編…

高級RAG策略學習(六)——Contextual Chunk Headers(CCH)技術

Contextual Chunk Headers&#xff08;CCH&#xff09;技術深度解析 第一部分&#xff1a;理論基礎與核心原理 一、核心定義&#xff1a;給 “文本塊” 加 “上下文標簽” Contextual Chunk Headers&#xff08;上下文塊標題&#xff0c;簡稱 CCH&#xff09;本質是為文檔拆分后…

人形機器人控制系統核心芯片從SoC到ASIC的進化路徑

目錄&#xff1a; 0 前言 1 人形機器人控制系統核心芯片選擇ASIC而非SoC的理由 1.1 SoC的架構特征 1.2 ASIC的架構特征 1.3 SoC的優勢&#xff08;繼承軟件生態&#xff09; 1.4 ASIC的優勢&#xff08;硬件底層算法就是應用層算法&#xff09; 1.5 人形機器人控制系統核…

linux thread 線程一

thread線程是linux的重要概念。線程不能獨立存在&#xff0c;必須在進程中存在。一個進程必須有一個線程&#xff0c;如果進程中沒有創建新線程&#xff0c;進程啟動后本身就有一個線程。使用getpid、getppid獲取進程的進程ID和父進程ID。使用pthread_self獲取到當前線程的ID。…

Arduino Nano33 BLESense Rev2【室內空氣質量檢測語音識別藍牙調光臺燈】

一、硬件介紹 1、產品特點 Arduino Nano 33 BLE Rev2&#xff0c;利用了nRF52840微控制器的先進功能。這款32位Arm Cortex-M4 CPU 64 MHz與MicroPython的兼容性增強了板子的靈活性&#xff0c;該開發板的突出特點是其藍牙低功耗&#xff08;BLE&#xff09;功能&#xff0c;使…

【問題解決】mac筆記本遇到鼠標無法點擊鍵盤可響應處理辦法?(Command+Option+P+R)

背景 如題。鼠標無法點擊&#xff0c;但可以移動。觸控板能夠波動&#xff0c;鼠標翻頁能夠work&#xff0c;但是點擊后無法響應。 根因 電腦緩存問題 解決辦法 重置PRAM&#xff1a; 確保電腦關機狀態&#xff08;可以先sudo shutdown -t now)&#xff08;一定要確保&#xff…

23ai數據庫通過SQLcl生成AWR報告

?1. 查看現有快照SQL> awr list snap;SNAP_ID DBID BEGIN_INTERVAL_TIME END_INTERVAL_TIME FLUSH_LEVEL __________ _____________ __________________________________ __________________________________ ______________793 …