【算法】——一鍵解決動態規劃

前言

動態規劃是一種高效解決??重疊子問題??和??最優子結構??問題的算法思想。它通過??分治+記憶化??,將復雜問題分解為子問題,并存儲中間結果,避免重復計算,從而大幅提升效率。

??為什么重要??

  1. ??優化暴力解法??:如斐波那契數列,遞歸復雜度為O(2n),而動態規劃可優化至O(n)。
  2. ??解決經典難題??:如背包問題、最短路徑、編輯距離等,動態規劃往往是??最優解法??。
  3. ??廣泛應用??:從算法競賽到實際開發(如資源調度、股票交易策略),動態規劃都是核心工具之一。

掌握動態規劃,能讓你在算法設計與優化中事半功倍!

動態規劃流程

我個人是覺得動態規劃是相當難的,因為我不太擅長找規律

動態規劃就像是我們熟知的找規律,通過已知項得出一個規律

再用這個規律,套用到已知項上,得出未知項

雖然難,但動態規劃也有自己的模板,讓你看到一個動態規劃有個思考方向

動態規劃流程

  1. 創建dp表,確定狀態表示
  2. 確定狀態轉移方程
  3. 初始化
  4. 順序填充
  5. 確定返回值

現在,分別介紹一下

創建dp表,并確定狀態表示

??DP表(動態規劃表)??是動態規劃算法的核心工具。

它本質是一個數組,其中的每一個元素都是一個解

但這個解的意義是未知的,需要我們自己去規定,即解的狀態表示

直接地

例如:Fn = Fn-1 + Fn-2

  • 我們創建一個dp表,此時dp[i]表示的值就是Fi的值

間接地

例如:求字符串中一個連續的無重復元素子串的最大長度

  • 我們創建一個dp表,此時dp[i]表示的值就是以i位置為結尾的無重復元素子串的最大長度

確定狀態轉移方程

如何從已知的dp[i]得到未知的dp[i],就需要得出狀態轉移方程

這就是找規律,也是動態規劃最難的一部分,得出正確的狀態轉移方程,是解決問題的關鍵部分

有些狀態轉移方程是明著告訴你的,有些則需要自己去找

這一步相當考驗你的經驗,解決這一步的唯一方法:多練多思考多畫圖

順序填充

dp表的數據元素代表解,我們求解問題,就是求出指定dp[i]

用狀態轉移方程求出dp[i],可能需要dp[i-1]、dp[i-2]等一個或者多個

有了前面,才有后面,因此必須順序填充

例如:Fn=Fn-1+Fn-2

  • 我們求一個dp[i],就需要先知道dp[i-1]和dp[i-2]的長度

初始化

進行順序填充前,需要先做好準備工作,防止順序填充的時候遇到錯誤

例如:Fn = Fn-1+Fn-2

當你填充dp[1]的時候,你就會發現根本沒有所謂的F1-2和F1-1,這就是越界錯誤

我們需要用初始化來避免越界錯誤

確定返回值

具體情況具體分析,根據題目要求確定返回值

例如:得出第幾項的值

  • 這時候直接返回dp[i]即可

再者:求字符串中一個連續的無重復元素子串的最大長度

  • 這時候就需要返回最大的dp[i]

動態規劃題型

斐波那契數列模型

斐波那契數列就是求出第幾個斐波那契數的值

這是最簡單的一類動態規劃題型,明明白白地告訴你怎么創建dp表,怎么進行狀態表示

屬于直接明牌了

流程解決:

  1. 創建dp表:創建一個大小為n+1的dp表,dp[i]表示的值即為F(i)
  2. 確定狀態轉移方程:F(n) = F(n-1) + F(n-2)
  3. 初始化:dp[0]=0,dp[1]=1
  4. 順序填充:從左往右依次填充
  5. 確定返回值:dp[n]表示的值就是F(n)的值,返回dp[n]即可

代碼如下

class Solution {
public:int fib(int n) {if(n==0) return 0;if(n==1) return 1;//創建dp表vector<int> dp(n+1);//初始化dp[0]=0;dp[1]=1;//順序填充for(int i=2;i<=n;i++){dp[i]=dp[i-1]+dp[i-2];}//返回結果return dp[n];}
};

路徑問題

求出到達目標地點有多少種方式

這種就是需要自己來找規律了

經典例題:不同路徑

?

流程解決:

創建dp表,確定狀態表示

  • 有多少個格子,dp表就需要多大,即dp表的大小就是m*n
  • 狀態表示有個技巧,題目最后要什么,你就表示什么,要求返回抵達最后一個位置的所有路徑總數,則dp[i][j]代表的就是這個抵達這個位置的所有路徑總數

確定狀態轉移方程

這里的狀態轉移方程就沒有明示了,需要我們自己去找

但也不難到達一個格子只有兩種方式,從正上方的格子下來,從左方的格子過來

而到達當前格子的正上方格子有多種方式,到達左方格子也有多種方式

因此,到達當前格子總路徑數 = 到達上方格子的路徑數 + 到達右方格子的路徑數

狀態轉移方程:dp[i][j] = dp[i][j-1] + dp[i-1][j]

初始化

  • dp[0][0]等于1

順序填充

  • 從左往右,從上往下進行填充
  • 填充的時候,需要注意左方格子和上方格子是否存在,進行取舍

確定返回值

  • 返回最后一個格子對應的dp[i][j]即可

代碼:

class Solution {
public:int uniquePaths(int m, int n) {//創建dp表vector<vector<int>> dp(m,vector<int>(n,0));//初始化dp[0][0]=1;for(int i=0;i<m;i++){for(int j=0;j<n;j++){if(i==0 && j!=0){dp[i][j]+=dp[i][j-1];}if(j==0 && i!=0){dp[i][j]+=dp[i-1][j];}else if(i!=0 && j!=0){dp[i][j]=dp[i-1][j]+dp[i][j-1];}}}return dp[m-1][n-1];}
};

簡單多狀態

在常規動態規劃問題中,每個子問題通常只需一個狀態表示(如dp[i])。但在??多狀態DP??中,每個步驟需要維護??多個并行的狀態??,通過它們之間的關系推導最終解。

??典型特征??:

  • 問題在每個步驟有??多種可能的狀態??(如"持有/未持有股票"、"偷/不偷當前房屋")
  • 需要為??每種狀態單獨建立DP表??或狀態變量
  • 狀態之間存在??相互轉移關系?

這是我個人問題有點難度的題型,因為你需要考慮多種狀態下的狀態表示

經典例題:打家劫舍

流程解決:

創建dp表

  • 創建dp表,dp表的大小即為給定房屋的個數,即為n
  • 狀態表示:dp[i]表示偷到當前房屋時的最大金額數

但此時房屋可能被偷,也可能沒有被偷!

  • 被偷時,該房屋的最大金額數應該加上當前房屋的金額
  • 沒有被偷時,該房屋的最大金額數則不應該加上當前房屋的金額

而一張dp表,是無法表示偷、不偷兩種狀態下的值的

因此,需要兩種dp表

  • dpf表,dpf[i]表示當前房屋被偷后,所獲得的最大金額
  • dpg表,dpg[i]表示當前房屋沒有被偷時,所獲得的最大金額

確定狀態轉移方程

當前房屋被偷

  • 相鄰的房屋一定沒有被偷

dpf狀態轉移方程:dpf[i] = dpg[i] +nums[i];

當前房屋沒有被偷,相鄰的房屋可能被偷,也可能沒有被偷

  • 上一個房屋沒有被偷時,狀態轉移方程:dpg[i] = dpg[i-1];
  • 上一個房屋被偷時,狀態轉移方程:dpg[i] = dpf[i-1]

dpg[i]表示當前房屋沒有被偷時的最大金額,因此取兩者最大即可

dpg狀態轉移方程:dpg[i] = max(dpg[i-1],dpf[i-1])

初始化

從開始位置開始

  • 偷,dpf[0] = nums[0]
  • 沒偷,dpg[0] = 0;

順序填充

  • 從左到右,依次填充兩個dp表

確定返回值

  • 返回最后一個房屋的最大金額即可,即max(dpf[n-1],dpg[n-1])

代碼:

class Solution {
public:int rob(vector<int>& nums) {if(nums.size()==0) return 0;//創建dp表int n = nums.size();vector<int> f(n);auto g = f;//初始化f[0]=nums[0];g[0]=0;//順序填充for(int i = 1;i<n;i++){f[i]=g[i-1]+nums[i];g[i]=max(g[i-1],f[i-1]);}return max(f[n-1],g[n-1]);}
};

子數組問題

子數組問題是動態規劃的經典應用場景,通常涉及??連續子數組??的最優解(如最大和、最長長度等)

??子數組問題的DP特點

  • ??連續性??:子數組要求元素連續,與子序列(可不連續)不同
  • ??單串DP??:通常用dp[i]表示??以第i個元素結尾的子數組的解??
  • ??狀態轉移??:要么延續前一個狀態,要么從當前元素重新開始

經典例題:最大子數組和

流程解決:

創建dp表,確定狀態表示

  • 創建一個大小和數組大小一樣的dp表
  • 狀態表示:dp[i]表示以i位置為結尾的子數組的最大和

確定狀態轉移方程

連續的子數組,所有下標必須連續,不能間斷

  • dp[i] = dp[i-1] + nums[i]

子數組可以是多個,也可以是一個,即從當前下標開始

  • dp[i] = nums[i]

dp[i]記錄的是以i位置為結尾的子數組的最大和,取兩者中的最大值

狀態轉移方程:dp[i] = max(dp[i-1]+nums[i],nums[i])

初始化

  • dp[0] = nums[0]

順序填充

  • 從左向右,依次對dp表進行填充

確定返回值

  • 我們需要的是該數組的最大子數組和,并不是最后一個位置的最大子數組和
  • 所以我們需要找到dp表中的最大值,并返回

代碼:

class Solution {
public:int maxSubArray(vector<int>& nums) {if(nums.size()==0) return 0;if(nums.size()==1) return nums[0];//創建dp表:dp[i]表示當前位置的最大連續子數組和int n = nums.size();vector<int> dp(n);//初始化dp[0]=nums[0];int ret = nums[0];//順序填充for(int i=1;i<n;i++){dp[i]=max(nums[i],dp[i-1]+nums[i]);if(dp[i]>ret) ret = dp[i];}return ret;}
};

子序列問題

子序列問題是動態規劃中的另一大類經典問題,與子數組問題最大的區別在于??元素不需要連續??。

經典例題:最長遞增子序列

流程解決:

創建dp表,確定狀態表示

  • 創建一個和數組大小一樣的dp表
  • 狀態表示:dp[i]的值表示以i位置為結尾的遞增子序列的最大長度

確定狀態轉移方程

  • 遞增子序列可以有多個元素,這是元素可以連續,也可以不連續
  • dp[i] = dp[j] + 1;

注意:這里的dp[j]可能并不與dp[i]相鄰,可以相鄰,也可以不相鄰,前提是滿足nums[j]<nums[i]

  • 遞增子序列也可能只有一個元素,即當前元素,代表之前沒有比其小的元素
  • dp[i] = 1

而dp[i]表示的是以i位置為結尾的遞增子序列的最大長度

很多人可能會覺得需要在這兩者中取最大值

但并不是,這里只能選擇符合要求的值

一旦前面沒有比當前元素小的元素,那么遞增子序列只能重頭開始,即長度為1

而如果有,則就需要再在原來的基礎長度上加1即可

所以,我們可以在創建dp表的時候,就將所有的dp[i]設置為1

后續如果nums[i]的前面有更小值,直接更新即可!

初始化

  • 不需要初始化

順序填充

  • 從左往右依次填充dp表

確定返回值

  • 返回dp表中的最大的dp[i]

回文串問題?

回文串問題是動態規劃的經典應用場景,通常涉及??子串/子序列的回文性質判斷??和??最值計算??。以下是系統性解題框架和典型例題分析。

??回文串問題的DP特點??

  • ??對稱性??:需判斷字符串的對稱性質(如s[i]==s[j]
  • ??中心擴展??:多數問題可轉化為??區間DP??(從短子串向長子串遞推)
  • ??狀態定義??:通常用dp[i][j]表示??子串s[i..j]的回文性質?

經典例題:回文子串

思路:

將給定字符串的所有子串進行枚舉,并對其每個進行判斷是否是回文串

這里的枚舉并不是真正的枚舉,而是進行一種映射

如:使用[i,j],表示一段區間

流程解決:

創建dp表,確定狀態表示

  1. 根據給定的字符串大小n,創建一個n*n的dp表
  2. 狀態表示:dp[i,j]:表示s中區間為[i,j]的子串是回文字符串

例如:s="abcter"?

dp[0][2] 表示"abc"是否是回文字符串

確定狀態轉移方程

判斷 s 的區間[i,j]是否是回文子串

如果 s[i] == s[j] , 則分三種情況進行判斷?

  1. i == j,[i,j]代表一個字符,則dp[i][j]=true
  2. i+1 == j,[i,j]代表兩個相鄰的字符,則dp[i,j]=true
  3. i+1 <j,代表不相鄰的兩個字符相同,接下來看看dp[i+1][j-1]是否是回文字符串

所以,狀態轉移方程:dp[i][j] = i+1<j?dp[i+1][j-1]:true;

初始化

  • 將dp表中的所有元素置為false

順序填充

  • 這里的順序填充不在是我們正常思維的順序,而是倒序
  • 在狀態轉移方程中,我們可以發現,求dp[i][j]可能需要dp[i+1][j-1],而dp[i+1][j-1]在dp[i][j]的左下方
  • 因此填充順序是從下到上,從左到右

確定返回值

  1. 首先設置一個計數器
  2. 遍歷一遍dp表,遇到true就讓計數器+1
  3. 最后返回計數器的值即可

結語

? 動態規劃是一種將復雜問題分解為相互關聯的子問題的算法思想,其核心在于利用最優子結構和避免重復計算來提升效率。我們通過定義狀態、建立轉移方程、初始化邊界條件和確定計算順序這四個關鍵步驟,可以系統性地解決各類動態規劃問題。無論是求最值、處理序列問題,還是解決背包或狀態機問題,動態規劃都展現出強大的建模能力。記住,許多看似復雜的問題,往往都能通過尋找子問題之間的遞推關系來優雅解決。動態規劃的魅力就在于它用空間換時間的智慧,讓原本可能指數級復雜度的問題變得可解。

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

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

相關文章

uniApp開發微信小程序-連接藍牙連接打印機上岸!

歷經波折三次成功上岸&#xff01; 三次經歷簡單絮叨一下&#xff1a;使用uniAppvue開發的微信小程序&#xff0c;使用藍牙連接打印機&#xff0c;藍牙所有的接口都是插件中封裝的&#xff0c;用的插件市場中的這個&#xff1a; dothan-lpapi-ble &#xff1b;所以&#xff0c…

軟件系統安全設計方案,信息化安全建設方案(Word原件)

1.1 總體設計 1.1.1 設計原則 1.2 物理層安全 1.2.1 機房建設安全 1.2.2 電氣安全特性 1.2.3 設備安全 1.2.4 介質安全措施 1.3 網絡層安全 1.3.1 網絡結構安全 1.3.2 劃分子網絡 1.3.3 異常流量管理 1.3.4 網絡安全審計 1.3.5 網絡訪問控制 1.3.6 完…

wsl2+ubuntu22.04安裝blenderproc教程

本章教程,介紹如何在windows操作系統上通過wsl2+Ubuntu22.04上安裝blenderproc。 一、pipi安裝方式 推薦使用minconda3安裝Python環境。 pip install Blenderproc二、源碼安裝 1、下載源碼 git clone https://github.com/DLR-RM/BlenderProc2、安裝依賴 cd BlenderProc &am…

Blender 轉 STL 文件全攻略:從基礎到進階

在 3D 建模與打印領域&#xff0c;Blender 憑借其強大的功能和開源特性&#xff0c;深受創作者喜愛。而 STL 文件格式&#xff0c;作為 3D 打印行業的通用標準&#xff0c;能被絕大多數 3D 打印軟件和設備所識別。因此&#xff0c;將 Blender 模型轉換為 STL 文件&#xff0c;是…

Ansys Electronics 變壓器 ACT

你好&#xff0c; 在本博客中&#xff0c;我將討論如何使用 Ansys 電子變壓器 ACT 自動快速地設計電力電子電感器或變壓器。我將逐步介紹設計和創建電力電子變壓器示例的步驟&#xff0c;該變壓器為同心組件&#xff0c;雙繞組&#xff0c;采用正弦電壓激勵&#xff0c;并應用…

nacos配置達夢數據庫驅動源代碼步驟

1.在父工程pom.xml添加依賴&#xff1a; <dependency><groupId>com.dameng</groupId><artifactId>DmJdbcDriver18</artifactId><version>8.1.1.193</version> </dependency> 2.在nacos-config模塊pom.xml添加依賴&#xff1…

4.9-4.10學習總結 Stream流練習+方法引用+異常

Stream流練習&#xff1a; 1.打印數組內的偶數。 import java.util.*; import java.util.function.BiConsumer; public class test {public static void main(String[] args) {ArrayList<Integer> listnew ArrayList<>();Collections.addAll(list,1,2,3,4,5,6,7,…

FPGA系統開發板調試過程不同芯片的移植步驟介紹

目錄 1.我目前使用的開發板 2.不同開發板的移植 步驟一&#xff1a;芯片型號設置 步驟二&#xff1a;約束修改 步驟三、IP核更新 關于FPGA系統開發板調試過程中不同芯片的移植。我需要先理清楚FPGA開發中移植到不同芯片的一般流程。首先&#xff0c;移植通常涉及到更換FPG…

復現QGIS-MCP教程

由于Claude國內下載不了嘗試使用Cursor 下載安裝Cursor Cursor - The AI Code Editor 本示例安裝的是0.46版本 UV安裝 簡介 安裝 安裝成功 配置環境變量 驗證 下載代碼 git clone gitgithub.com:jjsantos01/qgis_mcp.git QGIS插件安裝 文件拷貝 您需要將 qgis_mcp_plu…

java筆記03

基本數據類型 數據值是存儲在自己的空間中。 特點&#xff1a;賦值給其他變量&#xff0c;也是賦的真實的值。 引用數據類型 數據值是存儲在其他空間中&#xff0c;自己空間中存儲的是地址值。 特點&#xff1a;賦值給其他變量&#xff0c;賦的地址值。 綜合練習 使用 ctrl…

【開發工具】快速自定義圖標元素的顏色

如果你想要一個輕量級、簡單易用 的小工具來快速自定義圖標元素的顏色&#xff08;比如調整 SVG/PNG 圖標的顏色&#xff0c;或者生成多色圖標&#xff09;&#xff0c;可以試試以下工具&#xff1a; 1. 在線工具&#xff08;無需安裝&#xff09; SVG/PNG 圖標改色 - Recol…

【CompletableFuture】異步編程

CompletableFuture異步編程 CompletableFuture介紹與傳統 Future 的對比使用方法1. 使用 supplyAsync&#xff08;有返回值&#xff09;使用 runAsync&#xff08;無返回值&#xff09;指定自定義線程池 處理異步結果1. thenApply&#xff1a;轉換結果2.thenAccept&#xff1a;…

【TS學習】(23)理解類的雙重角色

在 TypeScript 中&#xff0c;類&#xff08;class&#xff09;不僅是一個運行時的值&#xff08;即可以實例化對象的構造函數&#xff09;&#xff0c;同時也是一個類型聲明。具體來說&#xff0c;類在 TypeScript 中既聲明了值&#xff0c;也聲明了類型&#xff0c;并且它的類…

IAP Firmware Upload Tools.exe IAP 網絡固件升級教程

IAP是In Application Programming的簡寫&#xff0c;IAP升級可以被視為固件升級的一種形式,它是一種在應用程序運行過程中對固件進行更新的技術手段。允許MCU在運行過程中對MCU User Flash的部分區域進行燒寫,目的是為了代替編程器對MCU燒錄的依賴。 主程序UI 軟件按鈕說明&a…

Uniapp當中的async/await的作用

一、原始代碼的行為&#xff08;使用 async/await&#xff09; const getUserMessagePlan async () > {// 等待兩個異步操作完成const tabsList await message.getTagesList(); // 等待獲取標簽列表const tagsStateList await message.getTagsStateList(); // 等…

設計模式 Day 5:夯實觀察者模式(Boost 實戰精講)

今天我們繼續深入觀察者模式的學習&#xff0c;不再局限于手寫的抽象結構&#xff0c;而是聚焦于真實項目中如何使用成熟框架&#xff08;如 Boost.Signals2&#xff09;高效落地觀察者模式。 本篇采用**“理論解析 問答講解 實戰用例”**結構&#xff0c;幫助你從設計思想到…

設計模式 Day 3:抽象工廠模式(Abstract Factory Pattern)詳解

經過前兩天的學習&#xff0c;我們已經掌握了單例模式與工廠方法模式&#xff0c;理解了如何控制實例個數與如何通過子類封裝對象的創建邏輯。 今天&#xff0c;我們將進一步深入“工廠”體系&#xff0c;學習抽象工廠模式&#xff08;Abstract Factory Pattern&#xff09;&a…

MySQL:事務的理解

一、CURD不加控制&#xff0c;會有什么問題 &#xff08;1&#xff09;因為&#xff0c;MySQL里面存的是數據&#xff0c;所以很有可能會被多個客戶訪問&#xff0c;所以mysqld可能一次會接受到多個關于CURD的請求。&#xff08;2&#xff09;且mysql內部是采用多線程來完成數…

藍橋杯刷題--寶石組合

在一個神秘的森林里&#xff0c;住著一個小精靈名叫小藍。有一天&#xff0c;他偶然發現了一個隱藏在樹洞里的寶藏&#xff0c;里面裝滿了閃爍著美麗光芒的寶石。這些寶石都有著不同的顏色和形狀&#xff0c;但最引人注目的是它們各自獨特的 “閃亮度” 屬性。每顆寶石都有一個…

DAY06:【pytorch】圖像增強

1、基本概念 數據增強&#xff0c;又稱數據增廣、數據擴增&#xff0c;是對訓練集進行變換&#xff0c;使訓練集更豐富&#xff0c;從而讓模型更具泛化能力 2、裁剪 — — Crop 2.1 transforms.CenterCrop 功能&#xff1a;從圖像中心裁剪圖片 size&#xff1a;所需裁剪圖…