【動態規劃算法】斐波那契數列模型

一. (1137.)第N個泰波那契數(力扣)

在這里插入圖片描述

1.1動態規劃的算法流程

對于初學者來講學術上的概念晦澀難懂,將用通俗易懂的方式帶來感性的理解.

1.狀態表示

dp表(一維或二維數組)里面的值所表示的含義
從哪獲取?
1.題目要求,如本題
2.題目沒有明確說明的情況下+做題經驗的累積
3.分析問題的過程中,發現重復的子問題來概括

2.狀態轉移方程

就是dp表元素等于什么,是一種遞推關系式,本題已告知

3.初始化

要確保填dp表時不越界,對填表時越界的元素首先初始化

4.填表順序

為了填寫當前狀態的時候,所需要的狀態已經計算過了的順序,本題為從左向右

5.返回值

取決于題目要求+狀態表示,本題為第n個數的值

class Solution {
public:int tribonacci(int n) {//處理邊界情況if(n==0) return 0;if(n==1||n==2) return 1;vector<int> dp(n+1);dp[0]=0,dp[1]=1,dp[2]=1;for(int i=3;i<=n;i++){dp[i]=dp[i-1]+dp[i-2]+dp[i-3];}return dp[n];}
};

6.空間優化

只會在本系列和背包問題中出現并分析,為了降低空間復雜度,能將一個級別
分析本題:滾動數組法
常規做法通過創建一個dp表數組來計算并存儲結果,但是通過遞推關系式得知想得到當前數的值只需要知道前面三個元素的值即可,那么另外不需要的空間就相當于浪費,所以可以考慮用四個變量來記錄,每次更新后三個變量來進行下一次計算,如此循環往復
在這里插入圖片描述
要注意更新變量的數據順序問題,避免被覆蓋

做法1:
class Solution {
public:int tribonacci(int n) {//處理邊界情況if(n==0) return 0;if(n==1||n==2) return 1;//優化int k=3;//初始計算次數,從第三次開始計算int a=0,b=1,c=1,d=0;while(k<=n)//判斷計算次數,返回條件{d=a+b+c;//更新變量a=b,b=c,c=d;k++;}return d;}
};做法2(不用控制計算次數):
class Solution {
public:int tribonacci(int n) {if(n == 0) return 0;if(n == 1 || n == 2) return 1;int a = 0, b = 1, c = 1, d = 0;for(int i = 3; i <= n; i++){d = a + b + c;a = b; b = c; c = d;}return d;}};

二. ?試題 08.01. 三步問題(力扣)

在這里插入圖片描述

2.1算法原理

  1. 狀態表?

這道題可以根據「經驗 + 題?要求」直接定義出狀態表?:
dp[i] 表?:到達 i 位置時,?共有多少種?法。

  1. 狀態轉移?程

以 i 位置狀態的最近的?步,來分情況討論:
如果 dp[i] 表??孩上第 i 階樓梯的所有?式,那么它應該等于所有上?步的?式之和:(一次最多上三級臺階)
i. 上?步上?級臺階, dp[i] += dp[i - 1] ;
ii. 上?步上兩級臺階, dp[i] += dp[i - 2] ;
iii. 上?步上三級臺階, dp[i] += dp[i - 3] ;
綜上所述, dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3]
需要注意的是,這道題?說,由于結果可能很?,需要對結果取模。在計算的時候,三個值全部加起來再取模,即 (dp[i - 1] + dp[i - 2] + dp[i - 3]) %MOD 是不可取的, n 取題?范圍內最?值時,?站會報錯 signed integer overflow 。
對于這類需要取模的問題,我們每計算?次(兩個數相加/乘等),都需要取?次模。否則,萬?發?了溢出,答案就錯了

  1. 初始化

從我們的遞推公式可以看出, dp[i] 在 i = 0, i = 1 以及 i = 2 的時候是沒有辦法進?推導的,因為 dp[-3] dp[-2] 或 dp[-1] 不是?個有效的數據。因此我們需要在填表之前,將 0,1, 2位置的值初始化。根據題意,, dp[0] = 1, dp[1] = 1, dp[2] = 2。

  1. 填表順序

爬臺階從下往上,對應數組從左往右,毫?疑問是「從左往右」。

  1. 返回值

應該返回 dp[n] 的值。

class Solution {
public:int waysToStep(int n) {//判斷邊界情況if(n==0|n==1) return 1;if(n==2) return 2;vector<int> dp(n+1);//初始化dp[0]=1,dp[1]=1,dp[2]=2;for(int i=3;i<=n;i++){//取模操作防止數據溢出dp[i]=((dp[i-1]+dp[i-2])%1000000007+dp[i-3])%1000000007;}return dp[n];}
};

三. (746.) 使?最?花費爬樓梯(力扣)

在這里插入圖片描述
注意:數組內的每?個下標 [0, n - 1] 表?的都是樓層,?頂樓的位置其實是在 n 的位置!

3.1算法原理

解法1

1.狀態表示

dp[i] 表?:到達 i 位置時的最?花費。(注意:到達 i 位置的時候, i 位置的錢不需要算上)

2.狀態轉移方程

根據最近的?步,分情況討論:
? 先到達 i - 1 的位置,然后?付 cost[i - 1] ,接下來??步?到 i 位置:dp[i - 1] + csot[i - 1]
? 先到達 i - 2 的位置,然后?付 cost[i - 2] ,接下來??步?到 i 位置:dp[i - 2] + csot[i - 2]

  1. 初始化:

從遞推公式可以看出,需要先初始化 i = 0 ,以及 i = 1 位置的值。容易得到dp[0] = dp[1] = 0 ,因為不需要任何花費,就可以直接站在第 0 層和第 1 層上

  1. 填表順序:

根據「狀態轉移?程」可得,遍歷的順序是「從左往右」

  1. 返回值:

根據「狀態表?以及題?要求」,需要返回 dp[n] 位置的值。

class Solution {
public:int minCostClimbingStairs(vector<int>& cost) {int n=cost.size();vector<int> dp(n+1);//初始化dp[0]=dp[1]=0;for(int i=2;i<=n;i++){dp[i]=min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2]);}return dp[n];}
};

解法2

  1. 狀態表?:

dp[i] 表?:從 i 位置出發,到達樓頂,此時的最?花費。

  1. 狀態轉移?程:

根據最近的?步,分情況討論:
? ?付 cost[i] ,往后??步,接下來從 i + 1 的位置出發到終點: dp[i + 1] + cost[i]
? ?付 cost[i] ,往后?兩步,接下來從 i + 2 的位置出發到終點: dp[i + 2] + cost[i]
我們要的是最?花費,因此 dp[i] = min(dp[i + 1], dp[i + 2]) + cost[i] 。

  1. 初始化:

為了保證填表的時候不越界,我們需要初始化最后兩個位置的值,結合狀態表?易得: dp[n - 1] = cost[n - 1], dp[n - 2] = cost[n - 2]

  1. 填表順序:

根據「狀態轉移?程」可得,遍歷的順序是「從右往左」。

  1. 返回值:

根據「狀態表?以及題?要求」,需要返回 dp[n] 位置的值。注意,此時不需要多開辟一個空間,因為根據狀態表示從n位置到n位置原地踏步的最小花費為0

class Solution {
public:int minCostClimbingStairs(vector<int>& cost) {int n=cost.size();vector<int> dp(n);//初始化dp[n-1]=cost[n-1];dp[n-2]=cost[n-2];for(int i=n-3;i>=0;i--)//從右往左初始化{dp[i]=min(dp[i+1]+cost[i],dp[i+2]+cost[i]);}return min(dp[0],dp[1]);}
};

注意狀態表示可以是多個的,如果能推得狀態轉移方程并且最后運行通過,證明該種狀態表示是正確的

四. (91.) 解碼?法(力扣)

4.1算法原理

  1. 狀態表?:

根據以往的經驗,對于?多數線性 dp ,經驗上都是「以某個位置結束或者開始」做?章,這?繼續嘗試「? i 位置為結尾」結合「題?要求」來定義狀態表?。dp[i] 表?:字符串中 [0,i] 區間上,?共有多少種編碼?法。

2.狀態轉移方程:

關于 i 位置的編碼狀況,我們可以分為下?兩種情況:
i. 讓 i 位置上的數單獨解碼成?個字?;
ii. 讓 i 位置上的數與 i - 1 位置上的數結合,解碼成?個字?
在這里插入圖片描述
由狀態定義不用考慮i+1位置的編碼,i和i-1位置的解碼都有成功和失敗兩種可能。
解碼成功:
解碼方法等于前一位區間上的解碼方法,相當于前一位區間上所由解碼結果后面再加上當前位置解碼后的字母即可
解碼失敗:
說明當前位置上不能單獨解碼或結合解碼,之前的努力都白費了

3.初始化

由于可能要?到 i - 1 以及 i - 2 位置上的 dp 值,因此要先初始化「前兩個位置」。
初始化 dp[0] :結果有0,1
i. 當 s[0] == ‘0’ 時,沒有編碼?法,結果 dp[0] = 0 ;
ii. 當 s[0] != ‘0’ 時,能編碼成功, dp[0] = 1
初始化 dp[1] :結果有0,1,2
i. 當 s[1] 在 [1,9] 之間時,能單獨編碼,此時 dp[1] += dp[0] (原因同上,
dp[1] 默認為 0 )
ii. 當 s[0] 與 s[1] 結合后的數在 [10, 26] 之間時,說明在前兩個字符中,?有?種
編碼?式,此時 dp[1] += 1

  1. 填表順序:

「從左往右」

  1. 返回值:

應該返回 dp[n - 1] 的值,表?在 [0, n - 1] 區間上的編碼?法。

class Solution {
public:int numDecodings(string s) {int n=s.size();vector<int> dp(n);//初始化if(s[0]!='0') dp[0]=1;//處理邊界情況if(n==1) return dp[0];if(s[0]!='0'&&s[1]!='0') dp[1]=1;//注意條件判斷時不能連續比較int t=(s[0]-'0')*10+(s[1]-'0');if(10<=t&&t<=26) dp[1]+=1;//填表for(int i=2;i<n;i++){//解碼當前位置,單獨if(s[i]!='0') dp[i]+=dp[i-1];//與上一位置共同解碼int t=(s[i-1]-'0')*10+(s[i]-'0');if(10<=t&&t<=26) dp[i]+=dp[i-2];}return dp[n-1];}
};

可以看出上述代碼初始化部分和循環部分代碼有些重復,特別是初始化dp[1]位置時有三種情況需要判斷,可以優化

4.2細節優化

dp表中多開辟一位,將dp[0]設置為輔助節點來初始化,這樣原dp表中dp[1]位置初始化的復雜條件判斷就可以避免,放到循環中去完成賦值,簡化了一次操作
在這里插入圖片描述

注意:
1.輔助節點中的值要保證后面的填表是正確的

已知會先初始化兩個節點,一個為輔助節點(新dp表中dp[0]),另一個為舊dp表中的dp[0]新dp表中的dp[1]。填表前會先判斷解碼,單獨解碼時與前一個節點有關與輔助節點無關,但與前一節點共同解碼時成功后會加上前前節點的解碼方法數,所以此時與輔助節點有關,該情況下輔助節點應設為1而不是0

2.下標的映射關系

由于dp表中多增一位,在查找原表進行解碼時下標位置要-1才能相對應

class Solution {
public:int numDecodings(string s) {int n=s.size();vector<int> dp(n+1);//初始化dp[0]=1;// 保證后續填表是正確的dp[1] = s[0] != '0';//填表for(int i=2;i<=n;i++){//解碼當前位置,單獨if(s[i-1]!='0') dp[i]+=dp[i-1];//與上一位置共同解碼int t=(s[i-2]-'0')*10+(s[i-1]-'0');//改變下標映射關系if(10<=t&&t<=26) dp[i]+=dp[i-2];}return dp[n];}
};

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

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

相關文章

Odoo 18 PWA 全面掌握:從架構、實現到高級定制

本文旨在對 Odoo 18 中的漸進式網絡應用&#xff08;Progressive Web App, PWA&#xff09;技術進行一次全面而深入的剖析。本文的目標讀者為 Odoo 技術顧問、高級開發人員及解決方案架構師&#xff0c;旨在提供一份權威的技術參考&#xff0c;以指導 PWA 相關的實施項目與戰略…

Binary Classifier Optimization for Large Language Model Alignment

2025.acl-long.93.pdfhttps://aclanthology.org/2025.acl-long.93.pdf 1. 概述 在生產環境中部署大型語言模型(LLMs)時,對齊LLMs一直是一個關鍵因素,因為預訓練的LLMs容易產生不良輸出。Ouyang等人(2022)引入了基于人類反饋的強化學習(RLHF),該方法涉及基于單個提示的…

在CentOS上以源碼編譯的方式安裝PostgreSQL

下載目錄&#xff1a;PostgreSQL: File Browser&#xff0c;我使用的PostgreSQLv17.5。Linux系統&#xff1a;CentOS Linux release 7.9.2009 (Core) 安裝依賴包和工具鏈&#xff08;必須且重要&#xff01;&#xff09; yum groupinstall "Development Tools" -y yu…

Baumer工業相機堡盟工業相機如何通過YoloV8深度學習模型實現沙灘小人檢測識別(C#代碼UI界面版)

Baumer工業相機堡盟工業相機如何通過YoloV8深度學習模型實現沙灘小人檢測識別&#xff08;C#代碼UI界面版&#xff09;工業相機使用YoloV8模型實現沙灘小人檢測識別工業相機通過YoloV8模型實現沙灘小人檢測識別的技術背景在相機SDK中獲取圖像轉換圖像的代碼分析工業相機圖像轉換…

Ubuntu服務器安裝與運維手冊——操作純享版

本手冊匯總了從硬件預配置、Ubuntu 安裝、網絡與服務配置&#xff0c;到 Windows/macOS 訪問共享、MySQL 初始化的完整流程&#xff0c;便于今后運維參考。 目錄 環境與硬件概覽BIOS/UEFI 設置制作與啟動安裝介質Ubuntu 24.04 LTS 安裝流程靜態 IP 配置&#xff08;netplan&am…

【Nginx】Nginx進階指南:解鎖代理與負載均衡的多樣玩法

在Web服務的世界里&#xff0c;Nginx就像是一位多面手&#xff0c;它不僅能作為高性能的Web服務器&#xff0c;還能輕松勝任代理服務器、負載均衡器等多種角色。今天&#xff0c;我們就來深入探索Nginx的幾個常見應用場景&#xff0c;通過實際案例和關鍵配置解析&#xff0c;帶…

原創-銳能微82xx系列電能計量芯片軟件驅動開發與精度校準流程完全指南

引言 電能計量芯片的軟件驅動開發是整個計量系統的核心&#xff0c;它直接決定了計量精度、系統穩定性和功能完整性。銳能微82xx系列電能計量芯片憑借其強大的數字信號處理能力和豐富的功能特性&#xff0c;為開發者提供了靈活的軟件開發平臺。本文將詳細介紹82xx系列芯片的軟…

如何使用 Apache Ignite 作為 Spring 框架的緩存(Spring Cache)后端

這份文檔是關于 如何使用 Apache Ignite 作為 Spring 框架的緩存&#xff08;Spring Cache&#xff09;后端&#xff0c;實現方法級別的緩存功能。 這和前面我們講的 Spring Data Ignite 是兩個不同的概念。我們先明確區別&#xff0c;再深入理解。&#x1f501; 一、核心區別…

Android 超大圖片、長圖分割加載

在Android開發中&#xff0c;處理大圖片的加載是一個常見且重要的問題&#xff0c;尤其是在需要顯示高分辨率圖片時。大圖片如果不正確處理&#xff0c;可能會導致內存溢出或應用性能下降。下面是一些常用的策略和技術來優化大圖片的加載&#xff1a;1. 使用圖片壓縮庫a. Glide…

Linux:理解操作系統

文章目錄數據流動操作系統數據流動 軟件運行&#xff0c;必須先加載到內存&#xff0c;本質要把磁盤上的文件 加載到內存。 我們寫的算法是處理存儲器里面的數據&#xff0c;數據就是文件&#xff0c;我們自己寫的可執行文件。 圖中QQ就是軟件&#xff0c;加載內存后進行下一步…

【每日一錯】PostgreSQL的WAL默認段大小

文章目錄題目擴展學習WAL工作原理流程圖題目 擴展學習 WAL&#xff08;Write Ahead Log&#xff09;預寫日志&#xff1a; WAL是PostgreSQL先寫日志、后寫數據的機制&#xff0c;用來防止數據丟失、提升數據恢復能力。 流程&#xff1a; 事務先寫日志文件&#xff08;WAL&…

Visual Studio Code 使用指南 (2025年版)

Visual Studio Code (VS Code) 是一款由微軟開發的免費、開源、跨平臺的現代化輕量級代碼編輯器&#xff0c;憑借其強大的核心功能、豐富的擴展生態系統以及高度可定制性&#xff0c;已成為全球數百萬開發者的首選工具。本指南旨在幫助您快速上手 VS Code&#xff0c;掌握其核心…

【Java】JVM虛擬機(java內存模型、GC垃圾回收)

一、Java內存模型&#xff08;JMM&#xff09;JMM&#xff08;Java Memory Model&#xff0c;Java 內存模型&#xff09;是 Java 虛擬機規范中定義的一種抽象概念&#xff0c;用于規范 Java 程序中多線程對共享內存的訪問規則&#xff0c;解決可見性、原子性和有序性問題&#…

二叉樹算法之【二叉樹的層序遍歷】

目錄 LeetCode-102題 LeetCode-102題 給定二叉樹的根節點root&#xff0c;返回其節點值的層序遍歷&#xff08;即逐層地&#xff0c;從左到右訪問所有節點&#xff09;。 class Solution {public List<List<Integer>> levelOrder(TreeNode root) {// checkif (r…

uniapp+vue3——通知欄標題縱向滾動切換

介紹 取巧&#xff0c;使用縱向輪播實現 <!-- 通知欄 --> <view class"noticeBox" v-if"notice.length>0"><image src"/static/images/index/noticeIcon.png" mode"aspectFill"></image><swiper class&…

BilldDesk 開源、免費、吊打收費軟件!白嫖黨最愛!遠程控制神器,沒有任何連接次數和畫質限制,同時顯示多屏、屏幕墻等高級功能

遠程控制軟件哪個好用&#xff1f;TeamViewer收費太貴&#xff0c;向日葵限制太多&#xff0c;QQ遠程又不穩定……別擔心&#xff01;今天給大家推薦一款完全免費、開源的遠程控制神器——BilldDesk&#xff01;它不僅功能強大&#xff0c;而且支持Windows、macOS、Linux、Andr…

ios UIAppearance 協議

一、前言 iOS 上提供了一個比較強大的工具UIAppearance&#xff0c;我們通過UIAppearance設置一些UI的全局效果&#xff0c;這樣就可以很方便的實現UI的自定義效果又能最簡單的實現統一界面風格。 (id)appearance ; 這個是這個協議里最重要的方法了 . 這個方法是統一全部改&am…

進階數據結構:用紅黑樹實現封裝map和set

? 嘿,各位技術潮人!好久不見甚是想念。生活就像一場奇妙冒險,而編程就是那把超酷的萬能鑰匙。此刻,陽光灑在鍵盤上,靈感在指尖跳躍,讓我們拋開一切束縛,給平淡日子加點料,注入滿滿的 passion。準備好和我一起沖進代碼的奇幻宇宙了嗎?Let’s go! 我的博客:yuanManGa…

【數據結構初階】--二叉樹(五)

&#x1f525;個人主頁&#xff1a;草莓熊Lotso &#x1f3ac;作者簡介&#xff1a;C研發方向學習者 &#x1f4d6;個人專欄&#xff1a; 《C語言》 《數據結構與算法》《C語言刷題集》《Leetcode刷題指南》 ??人生格言&#xff1a;生活是默默的堅持&#xff0c;毅力是永久的…

redis布隆過濾器解決緩存擊穿問題

在電商系統中&#xff0c;商品詳情頁是一個典型的高頻訪問場景。當用戶請求某個商品的詳情時&#xff0c;系統會優先從緩存中獲取數據。如果緩存中沒有該商品的詳情&#xff0c;系統會去數據庫查詢并更新緩存。然而&#xff0c;如果某個熱門商品的緩存失效&#xff0c;大量請求…