【動態規劃】--- 斐波那契數模型

?Welcome to 9ilk's Code World

? ? ? ?

(??? ? ???)?個人主頁:? ? ? ?9ilk

(??? ? ???)?文章專欄:? ? ?算法Journey?


🏠 第N個泰波那契數模型

📌 題目解析

第N個泰波那契數

  • 題目要求的是泰波那契數,并非斐波那契數。

📌 算法原理

🎵 遞歸

函數設置:我們可以設置一個Taibo()函數,它能幫我們求出第n個泰波那契數。

函數返回值:題目保證answer <= 2^31 - 1,設置為int即可。

函數實現:根據定義求泰波那契數,我們需要前三個的泰波那契數相加,也就是Taibo(n-3) + Taibo(n-2) + Taibo(n-1)。

函數邊界條件:對于n = 0,1,2是邊界情況,我們可以提前處理。

參考代碼:

class Solution {
public:long Taibo(int n){if(n == 0)return 0;if(n == 1 || n == 2)return 1;return Taibo(n-1) + Taibo(n-2) + Taibo(n-3);}int tribonacci(int n){return Taibo(n);}
};

🎵 記憶化搜索

對于解法一存在下面的問題:

我們發現在遞歸過程有的節點的值(比如上圖的Taibo(3))在第3層就已經求得了,但是其他節點遞歸深入時又重新計算了,導致了不必要的時間和棧空間的開銷(時間復雜度:O(3^n),空間復雜度:O(N))。本題雖然n最大為37,但其實棧空間和時間開銷已經很大了,肯定會超時,我們可以采取記憶化搜索的方法:

1. 添加一個備忘錄。

2. 每次遞歸返回時,將結果放到備忘錄里面。

3. 在每次進入遞歸時,往備忘錄里查詢是否已經記錄。

參考代碼:

class Solution {
public:vector<int> memory;long Taibo(int n){if (memory[n] != -1) //查看備忘錄return memory[n];long ret = Taibo(n - 1) + Taibo(n - 2) + Taibo(n - 3);;memory[n] = ret; //存進備忘錄return ret;}int tribonacci(int n){memory.resize(38, -1); //創建一個備忘錄memory[0] = 0;memory[1] = memory[2] = 1;return Taibo(n);}
};
  • 此時比之前大大避免了不必要的時間開銷,時間復雜度是(n)。

🎵 動態規劃

我們動態規劃分為以下幾步:

1. 狀態表示:

  • 所謂狀態表示其實是dp表里每個值代表的含義。

Q:狀態從何而來?

  • 題目要求(比如本題已經告訴我們要求的是第n個泰波那契數)。
  • 經驗 + 題目要求(后面我們再提)。
  • 分析問題的過程,發現重復的子問題。

因此,本題dp[i]的含義是第i個泰波那契數。

2. 狀態轉移方程:

狀態轉移方程回答的是dp[i]怎么得到的問題,一般我們從"最近一步"得到。

比如本題中,由定義知,在 n >= 0?的條件下 Tn+3?= Tn?+ Tn+1?+ Tn+2,不就是dp[i] = dp[i-1] + dp[i-2] + dp[i-3]嗎?

3. 初始化:

初始化保證我們填表的時候不發生越界!

當遇到n=2時,此時n-3=-1很明顯會會發生越界,因此對于邊界情況,我們可以提前確定好邊界位置在dp表中的值,即dp[0] = 0, dp[1] = dp[2] = 1。

4. 填表順序:

動態規劃需要狀態的推導,只有確定好填表順序,才能確保在填寫當前狀態時,所需要的狀態已經計算過了。

本題很明顯是從左往右填表

5. 返回值:

我們需要根據題目要求+狀態表示來確定返回值。

注:dp[i]不一定就是我們所需的返回值,我們還需結合題目要求,本題dp[n]就是我們需要的返回值。

參考代碼:

class Solution 
{
public:int tribonacci(int n){//1.狀態表示:dp[i]表示第N個泰波那切數vector<int> dp(38,-1);//2.初始化dp[0] = 0;dp[1] = dp[2] = 1;//3.填表for(int i = 3 ; i <= n ; i ++){dp[i] = dp[i-1] + dp[i-2] + dp[i-3];//4.狀態轉移方程} return dp[n];//5.返回值}
};

時間復雜度:O(N)

空間復雜度:O(1)

🎵 滾動數組

我們用vector容器來模擬dp表,但其實可以進一步優化,當求dp[i]只有前面的若干個狀態,最前面的幾個狀態不需要浪費空間,此時可以使用滾動數組優化

我們可以利用四個變量來儲存最近一次求泰波那契數的四個狀態,不斷進行滾動,最終求得目標結果!

注:對于賦值順序,不能從右往左賦,即c = d,b = c, a = b因為d給c之后,c被d覆蓋了,但是b想要的是c的,而c原本的值被覆蓋了!

參考代碼:

class Solution 
{
public:int tribonacci(int n){//1.狀態表示:dp[i]表示第N個泰波那切數if (n == 0) return 0;if (n == 1 || n == 2) return 1;int a = 0;int b = 1;int c = 1;int d = 0;//3.填表for (int i = 3; i <= n; i++){d = a + b + c;;//狀態轉移方程a = b;b = c;c = d;}return d;}
};

🏠 三步問題

📌 題目解析

三步問題

📌 算法原理

1. 狀態表示(經驗 + 題目要求):

  • 狀態表示:dp[i]表示到達i位置時,一共有多少種方法。

2. 狀態轉移方程:

我們從i位置的狀態,最近的一步來劃分問題,由于小孩一次可以上1階,2階,3階:

  • 從i-1位置上來,此時dp[i] += dp[i-1]。
  • 從i-2位置上來,此時dp[i] += dp[i-2]。
  • 從i-3位置上來,此時dp[i] += dp[i-3]。

因此狀態轉移方程:dp[i] = dp[i-1] + dp[i-2] + dp[i-3]

3. 初始化:

?對于第1,2,3級的臺階,取它們的最近狀態可能會造成數組越界(比如i為2時,i-3得-1會越界),因此我們可以提前設置好它們的狀態:dp[1] = 1 , dp[2] = 2,dp[3] = 4

4. 填表順序:

由狀態轉移方程知,我們i位置的狀態依賴于前幾個位置的狀態,因此我們填表順序是從左往右填。

5.返回值:

我們要求的是上到第n階樓梯的總方法,直接返回dp[n]即可,注意要對結果模1000000007

參考代碼:

class Solution 
{
public:int waysToStep(int n){if(n == 1 || n == 2) return n;if(n == 3) return 4;long a = 1; //dp[1]long b = 2; //dp[2] long c = 4; //dp[3]long d = 0; for(int i = 4 ; i <= n ; i ++) //空間優化{d = (a + b + c)%1000000007; //狀態轉移方程a = b;b = c;c = d;}      return d;}
};

🏠 最小花費爬樓梯

📌 題目解析

最小花費爬樓梯

  • 假設n為數組元素個數,則本題中樓梯頂部指的是dp[n],并非dp[n-1]。

📌 算法原理

🎵 解法一 (以i位置為結尾)

1. 狀態表示:

  • dp[i]表示:到達 i 位置時,所需支付的最少費用。

2. 狀態轉移方程:

用i位置的最近一步(之前或之后的狀態),推導出dp[i]的值。

  • 當到達i-1位置時,支付cost[i-1],走一步到達i位置 -> dp[i-1] + cost[i-1]。
  • 當到達i-2位置時,支付cost[i-2],走兩步到達i位置 -> dp[i-2] + cost[i-2]。
  • 我們要么選擇從i-1位置到i,要么選擇從i-2位置到i,我們要的是最小花費,則選最小的即可。

因此狀態轉移方程:dp[i] = min(dp[i-1]+cost[i-1] , dp[i-2] + cost[i-2])。

3.初始化

我們需要保證填表的時候不越界,本題可以選擇從下標為0或下標為1的位置開始爬樓梯,因此這兩個位置最初的花費是0,即dp[0] = dp[1] = 0。

4. 填表順序

根據我們的狀態轉移方程,我們需i位置之前的狀態,因此填表順序是從左往右填。

5. 返回值

返回達到樓梯頂部的最低花費,返回dp[n]即可。

參考代碼:

class Solution {
public:int minCostClimbingStairs(vector<int>& cost) {int n = cost.size();if(n == 1 || n == 0) return  0;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]; //返回值}
};

🎵 解法二?(以i位置為起點)
?

1. 狀態表示:

  • dp[i]表示:從i位置出發,所需支付的最少費用。

2. 狀態轉移方程:

用i位置的最近一步(之前或之后的狀態),推導出dp[i]的值。

  • 支付cost[i], 往后走一步,從i+1的位置出發到終點 -> dp[i+1] + cost[i]
  • 支付cost[i], 往后走兩步,從i+2的位置出發到終點 -> dp[i+2] + cost[i]
  • 我們從i位置要么選擇走一步到終點,要么選擇走兩步到終點,我們要的是最小花費,則選最小的即可。

因此狀態轉移方程:dp[i] = min(dp[i+1] + cost[i] , dp[i+2] + cost[i])。

3.初始化

對于n-1位置和n-2位置作為出發點,此時他們走一步或兩步就到頂部了,因此i+1和i+2會使他們越界,我們只需支付他們對應的cost即可,即dp[n-1] = cost[n-1] && dp[n-2] = cost[n-2]

4. 填表順序

根據我們的狀態轉移方程,我們需i位置之后的狀態,因此填表順序是從右往左填。

5. 返回值

我們是從0或1位置為起點出發的,我們返回兩者最小即可,即min(dp[0],dp[1])。

參考代碼:

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

🏠 解碼方法

📌 題目解析

91. 解碼方法 - 力扣(LeetCode)

  • 本題可能存在無法解碼的字符串。
  • 字符串中可能包含前導0。

📌 算法原理

1.狀態表示:

根據經驗+題目要求,我們可以設置dp[i]的狀態:字符串以i位置結尾時,解碼方法的總數

2. 狀態轉移方程:

我們還是按照最近一步來劃分問題,對于i位置的解碼我們以下兩種情況:

(1) s[i] 單獨解碼:

  • 解碼成功('1' <= s[i] <= '9',‘0’無法參與解碼),此時解碼總方法等于前一個位置的解碼方法總數,即dp[i-1]。?
  • 解碼失敗,此時為0

(2) s[i] 與 s[i-1]解碼

  • 解碼成功(0 <= b*10+a <= 26),時解碼總方法等于第i-2個位結尾字符串的解碼方法總數,即dp[i-2]。?
  • 解碼失敗,此時為0

因此狀態轉移方程為:dp[i] = dp[i-1] + dp[i-2]

3. 初始化:

(1) i = 0時,位于字符串的第一個字符,我們只需判斷它單獨解碼情況是否成立,取值可能為0,1。

(2) i = 1時,位于字符串的第二個字符,首先要單獨解碼就得先判斷第一個字符能否單獨解碼否則沒意義,能單獨解碼則dp[1]++;再判斷與s[0]是否能解碼,能則dp[1]++。其可能取值為0,1,2。

4. 狀態轉移方程 :

根據狀態轉移方程,我們需要之前位置的狀態,因此填表順序是從左往右。

5. 返回值:

由題意得,最終需要的是以size-1為位置結尾的字符串的所有解碼方法,因此返回dp[size-1]。

參考代碼:

class Solution {
public:int numDecodings(string s){int n = s.size();vector<int> dp(n);dp[0] = s[0] != '0';//初始化處理邊界if(n == 1) return dp[0];if(s[0] != '0' && s[1] != '0') dp[1] += 1;//s[1]單獨解碼int t = (s[0]-'0')*10 + s[1] - '0'; if(t >= 10 && t <= 26) dp[1] += 1 ;//s[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(t >= 10 && t <= 26) dp[i] += 1;}return dp[n-1];}
};

🎵 優化(虛擬節點)

Q:我們發現這兩段代碼相似度較高,處理邏輯是一樣的,能不能把邊界情況放進循環里處理呢?

這里我們介紹一下虛擬節點

我們可以在原dp表基礎上擴充一個位置,保證最后一個位置下標為n,這樣在處理字符串中原來下標為0位置的字符時,它在新dp表的下標變為1,這樣i-1就不會越界!但是同時要注意兩個問題:

1. 虛擬節點里面的值,要保證后面的填表時正確的。(比如對于新dp表的0下標位置,我們要保證對于如果字符串第二個位置的字符能跟第一個字符解碼,此時需要新dp表i-2位置的值,也就是dp[0],此時我們需要設置它為1,表示存在第二個字符和第二個字符共同解碼這一種解碼方法)

2. 下標的映射關系:我們新dp表下標在原來基礎上+1,但是s[i]的size并沒有變化!

class Solution
{
public:int numDecodings(string s)
{
//優化int n=s.size();vector<int>dp(n + 1);dp[0]=1;//保證后面的填表是正確的dp[1]= s[1 - 1] != '0';
注意映射關系s[1-1]下標映射關系for(inti=2;i<=n;i++){if(s[i-1]!='0')dp[i]+=dp[i-1];//處理單獨編碼的情況int =(s[i-2]-'0')*10+s[i-1]-'0';//第二種情況所對應的數if(t>=10 &&t<=26)dp[i]+=dp[i] += dp[i - 2];}return dp[n];
}

完。

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

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

相關文章

如何確保Spring單例Bean在高并發環境下的安全性?

在Spring中&#xff0c;單例Bean就像是一個“公共的水杯”&#xff0c;整個應用程序中的所有線程都會共享這一個實例。在大部分情況下&#xff0c;這沒什么問題&#xff0c;但如果多個線程同時想要修改這個“水杯”里的內容&#xff0c;就可能會出現問題了。 想象一下&#xff…

期刊審稿意見回復的LaTeX模板分享

下載網址 https://github.com/NeuroDong/Latex_for_review_comments 效果展示 分享內容 在學術寫作過程中&#xff0c;回復審稿意見是一個重要且繁瑣的環節。由于審稿人眾多&#xff0c;使用Word進行排版往往效率低下。為了提高效率&#xff0c;我在網上找到了一個LaTeX模板…

Vue 3 30天精進之旅:Day 03 - Vue實例

引言 在前兩天的學習中&#xff0c;我們成功搭建了Vue.js的開發環境&#xff0c;并創建了我們的第一個Vue項目。今天&#xff0c;我們將深入了解Vue的核心概念之一——Vue實例。通過學習Vue實例&#xff0c;你將理解Vue的基礎架構&#xff0c;掌握數據綁定、模板語法和指令的使…

在Vue中,<img> 標簽的 src 值

1. 直接指定 src 的值&#xff08;適用于網絡圖片&#xff09; 如果你使用的是網絡圖片&#xff08;即圖片的URL是完整的HTTP或HTTPS鏈接&#xff09;&#xff0c;可以直接指定 src 的值&#xff1a; vue 復制 <template><div><img src"https://exampl…

Spring Boot/MVC

一、Spring Boot的創建 1.Spring Boot簡化Spring程序的開發,使用注解和配置的方式開發 springboot內置了tomact服務器 tomact:web服務器,默認端口號8080,所以訪問程序使用8080 src/main/java:Java源代碼 src/main/resource:靜態資源或配置文件,存放前端代碼(js,css,html) s…

Spring--SpringMVC的調用流程

一.簡介 1.1主要作用 SSM框架構建起單的技術棧需求&#xff01;其中的SpringMVC負責表述層&#xff08;控制層&#xff09;實現簡化&#xff01; 最終總結&#xff1a; 1. 簡化前端參數接收( 形參列表 )2. 端數據響應(返回值)1.2核心組件和調用流程 Spring MVC與許多其他Web…

C#集合排序的三種方法(List<T>.Sort、LINQ 的 OrderBy、IComparable<T> 接口)

見過不少人、經過不少事、也吃過不少苦&#xff0c;感悟世事無常、人心多變&#xff0c;靠著回憶將往事串珠成鏈&#xff0c;聊聊感情、談談發展&#xff0c;我慢慢寫、你一點一點看...... 1、使用 List<T>.Sort 方法與自定義比較器 public class Person{public string …

從ChatGPT熱潮看智算崛起

2025年1月7日&#xff0c;科智咨詢發布《2025年IDC產業七大發展趨勢》&#xff0c;其中提到“ChatGPT開啟生成式AI熱潮&#xff0c;智能算力需求暴漲&#xff0c;算力供給結構發生轉變”。 【圖片來源于網絡&#xff0c;侵刪】 為何會以ChatGPT發布為節點呢&#xff1f;咱們一起…

Frida使用指南(三)- Frida-Native-Hook

1.Process、Module、Memory基礎 1.Process Process 對象代表當前被Hook的進程,能獲取進程的信息,枚舉模塊,枚舉范圍等 2.Module Module 對象代表一個加載到進程的模塊(例如,在 Windows 上的 DLL,或在 Linux/Android 上的 .so 文件), 能查詢模塊的信息,如模塊的基址、名…

Electron學習筆記,安裝環境(1)

1、支持win7的Electron 的版本是18&#xff0c;這里node.js用的是14版本&#xff08;node-v14.21.3-x86.msi&#xff09;云盤有安裝包 Electron 18.x (截至2023年仍在維護中): Chromium: 96 Node.js: 14.17.0 2、安裝node環境&#xff0c;node-v14.21.3-x86.msi雙擊運行選擇安…

漏洞修復:Apache Tomcat 安全漏洞(CVE-2024-50379) | Apache Tomcat 安全漏洞(CVE-2024-52318)

文章目錄 引言I Apache Tomcat 安全漏洞(CVE-2024-50379)漏洞描述修復建議升級Tomcat教程II Apache Tomcat 安全漏洞(CVE-2024-52318)漏洞描述修復建議III 安全警告引言 解決方案:升級到最新版Tomcat https://blog.csdn.net/z929118967/article/details/142934649 service in…

提示詞的藝術 ---- AI Prompt 進階(提示詞框架)

提示詞的藝術 ---- AI Prompt 進階&#xff08;提示詞框架&#xff09; 寫在前面 上周發布了一篇《提示詞的藝術----AI Prompt撰寫指南》&#xff0c;旨在幫助讀者理解提示詞的作用&#xff0c;以及簡單的提示詞撰寫指南。本篇作為進階內容&#xff0c;將給出常用的提示詞框架…

PyQt4 的圖片切割編輯器

一、 編輯器功能明確 允許用戶加載圖片、選擇切割模式、對切割后的圖片片段進行操作&#xff08;如移動、復制、粘貼、刪除等&#xff09;&#xff0c;并支持撤銷和重做操作。 環境&#xff1a;Py2.7 PyQt 4.11 二、導入模塊介紹 sys: 用于訪問與 Python 解釋器強相關的變…

[MySQL]數據庫表內容的增刪查改操作大全

目錄 一、增加表數據 1.全列插入與指定列插入 2.多行數據插入 3.更新與替換插入 二、查看表數據 1.全列查詢與指定列查詢 2.查詢表達式字段 3.為查詢結果起別名 4.結果去重 5.WHERE條件 6.結果排序 7.篩選分頁結果 8.插入查詢的結果 9.group by子句 三、修改表數…

在 Windows 11 中為 SMB 3.x 文件共享協議提供 RDMA 支持

注&#xff1a;機翻&#xff0c;未校。 Enable SMB Direct in Windows 11 在 Windows 11 中啟用 SMB Direct Provides RDMA support for the SMB 3.x file sharing protocol 為 SMB 3.x 文件共享協議提供 RDMA 支持 Vigneshwaran Vijayakumar November 3, 2024 Last Updat…

electron打包客戶端在rk3588上支持h265硬解

目錄 前言 chromium是如何支持h265硬解 electron/chromium第一次編譯 electron/chromium第二次編譯 前言 我們的客戶端程序是用electron打包的前端程序&#xff0c;其在rk3588主機上的linux環境運行。之前使用客戶端查看h264編碼的視頻直播是沒有問題的&#xff0c;但視頻源…

什么是網絡爬蟲?Python爬蟲到底怎么學?

最近我在研究 Python 網絡爬蟲&#xff0c;發現這玩意兒真是有趣&#xff0c;干脆和大家聊聊我的心得吧&#xff01;咱們都知道&#xff0c;網絡上的信息多得就像大海里的水&#xff0c;而網絡爬蟲就像一個勤勞的小礦工&#xff0c;能幫我們從這片浩瀚的信息海洋中挖掘出需要的…

【Jave全棧】Java與JavaScript比較

文章目錄 前言一、Java1、 歷史與背景2、語言特點3、應用場景4、生態系統 二、JavaScript1、歷史與背景2、語言特點3、應用場景4、 生態系統 三、相同點四、不同點1、語言類型2、用途3、語法和結構4、性能5、生態系統6、開發模式 前言 Java和JavaScript是兩種不同的編程語言&a…

GitCode 助力 AutoTable:共創 MyBatis 生態的自動表格管理新篇章

項目倉庫https://gitcode.com/dromara/auto-table 解放雙手&#xff0c;專注業務&#xff1a;MyBatis 生態的“自動表格”創新 AutoTable 是一款致力于為 MyBatis 生態賦予“自動表格”功能的創新插件。其核心理念是通過 Java 實體類自動生成和維護數據庫的表結構&#xff0c…

【MCU】DFU、IAP、OTA

我發現很多人把幾個概念都學混了&#xff0c;只記得一個升級了 DFU DFU (device firmware update)是指的 USB DFU&#xff0c;這個是 USB 的一個機制&#xff0c;可以升級設備的固件&#xff0c;可以去 USB-IF 查看規范文件。 OTA 全稱為 Over-the-air update&#xff0c;利…