代碼隨想錄算法訓練營第三十八天 | 理論基礎,509. 斐波那契數,70. 爬樓梯,746. 使用最小花費爬樓梯

代碼隨想錄算法訓練營第三十八天 | 理論基礎,509. 斐波那契數,70. 爬樓梯,746. 使用最小花費爬樓梯

  • 理論基礎
    • 什么是動態規劃
    • 動態規劃的解題步驟
    • 動態規劃應該如何debug
  • 509. 斐波那契數
    • 遞歸解法
  • 70. 爬樓梯
  • 746. 使用最小花費爬樓梯

理論基礎

在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
視頻講解

什么是動態規劃

如果某一問題有很多重疊子問題,使用動態規劃是最有效的,所以動態規劃中每一個狀態一定是由上一個狀態推導出來的,這一點就區分于貪心,貪心沒有狀態推導,而是從局部直接選最優的,例如:有N件物品和一個最多能背重量為W 的背包,第i件物品的重量是weight[i],得到的價值是value[i],每件物品只能用一次,求解將哪些物品裝入背包里物品價值總和最大,動態規劃中dp[j]是由dp[j-weight[i]]推導出來的,然后取max(dp[j], dp[j - weight[i]] + value[i]),但如果是貪心呢,每次拿物品選一個最大的或者最小的就完事了,和上一個狀態沒有關系,所以貪心解決不了動態規劃的問題其實大家也不用死扣動規和貪心的理論區別,后面做做題目自然就知道了,而且很多講解動態規劃的文章都會講最優子結構啊和重疊子問題啊這些,這些東西都是教科書的上定義,晦澀難懂而且不實用,大家知道動規是由前一個狀態推導出來的,而貪心是局部直接選最優的,對于刷題來說就夠用了,上述提到的背包問題,后序會詳細講解

動態規劃的解題步驟

對于動態規劃問題,我將拆解為如下五步曲,這五步都搞清楚了,才能說把動態規劃真的掌握了!

  1. 確定dp數組以及下標的含義
  2. 確定遞推公式
  3. dp數組如何初始化
  4. 確定遍歷順序
  5. 舉例推導dp數組

動態規劃應該如何debug

找問題的最好方式就是把dp數組打印出來,看看究竟是不是按照自己思路推導的!做動規的題目,寫代碼之前一定要把狀態轉移在dp數組的上具體情況模擬一遍,心中有數,確定最后推出的是想要的結果

509. 斐波那契數

題目鏈接
視頻講解
斐波那契數 (通常用 F(n) 表示)形成的序列稱為 斐波那契數列,該數列由 0 和 1 開始,后面的每一項數字都是前面兩項數字的和,也就是:
F(0) = 0,F(1) = 1
F(n) = F(n - 1) + F(n - 2),其中 n > 1
給定 n ,請計算 F(n)

輸入:n = 4
輸出:3

動規五部曲:
這里我們要用一個一維dp數組來保存遞歸的結果,確定dp數組以及下標的含義,dp[i]的定義為:第i個數的斐波那契數值是dp[i],確定遞推公式,為什么這是一道非常簡單的入門題目呢?因為題目已經把遞推公式直接給我們了:狀態轉移方程 dp[i] = dp[i - 1] + dp[i - 2];dp數組如何初始化,題目中把如何初始化也直接給我們了,如下:

dp[0] = 0;
dp[1] = 1;

確定遍歷順序
從遞歸公式dp[i] = dp[i - 1] + dp[i - 2];中可以看出,dp[i]是依賴 dp[i - 1] 和 dp[i - 2],那么遍歷的順序一定是從前到后遍歷的,舉例推導dp數組,按照這個遞推公式dp[i] = dp[i - 1] + dp[i - 2],我們來推導一下,當N為10的時候,dp數組應該是如下的數列:
0 1 1 2 3 5 8 13 21 34 55
如果代碼寫出來,發現結果不對,就把dp數組打印出來看看和我們推導的數列是不是一致的

class Solution {
public:int fib(int N) {if (N <= 1) return N;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];}
};

當然可以發現,我們只需要維護兩個數值就可以了,不需要記錄整個序列
代碼如下:

class Solution {
public:int fib(int N) {if (N <= 1) return N;int dp[2];dp[0] = 0;dp[1] = 1;for (int i = 2; i <= N; i++) {int sum = dp[0] + dp[1];dp[0] = dp[1];dp[1] = sum;}return dp[1];}
};

遞歸解法

class Solution {
public:int fib(int N) {if (N < 2) return N;return fib(N - 1) + fib(N - 2);}
};

70. 爬樓梯

題目鏈接
視頻講解
假設你正在爬樓梯,需要 n 階你才能到達樓頂,每次你可以爬 1 或 2 個臺階,你有多少種不同的方法可以爬到樓頂呢?

輸入:n = 3
輸出:3

本題如果沒有接觸過的話,會感覺比較難,多舉幾個例子,就可以發現其規律,爬到第一層樓梯有一種方法,爬到二層樓梯有兩種方法,那么第一層樓梯再跨兩步就到第三層 ,第二層樓梯再跨一步就到第三層,所以到第三層樓梯的狀態可以由第二層樓梯 和 到第一層樓梯狀態推導出來,那么就可以想到動態規劃了
我們來分析一下,動規五部曲:
定義一個一維數組來記錄不同樓層的狀態
1.確定dp數組以及下標的含義
dp[i]: 爬到第i層樓梯,有dp[i]種方法
2.確定遞推公式
如何可以推出dp[i]呢?從dp[i]的定義可以看出,dp[i] 可以有兩個方向推出來,首先是dp[i - 1],上i-1層樓梯,有dp[i - 1]種方法,那么再一步跳一個臺階不就是dp[i]了么,還有就是dp[i - 2],上i-2層樓梯,有dp[i - 2]種方法,那么再一步跳兩個臺階不就是dp[i]了么,那么dp[i]就是 dp[i - 1]與dp[i - 2]之和!所以dp[i] = dp[i - 1] + dp[i - 2],在推導dp[i]的時候,一定要時刻想著dp[i]的定義,否則容易跑偏這體現出確定dp數組以及下標的含義的重要性!
3.dp數組如何初始化
再回顧一下dp[i]的定義:爬到第i層樓梯,有dp[i]種方法,那么i為0,dp[i]應該是多少呢,這個可以有很多解釋,但基本都是直接奔著答案去解釋的,例如強行安慰自己爬到第0層,也有一種方法,什么都不做也就是一種方法即:dp[0] = 1,相當于直接站在樓頂,但總有點牽強的成分,那還這么理解呢:我就認為跑到第0層,方法就是0啊,一步只能走一個臺階或者兩個臺階,然而樓層是0,直接站樓頂上了,就是不用方法,dp[0]就應該是0.其實這么爭論下去沒有意義,大部分解釋說dp[0]應該為1的理由其實是因為dp[0]=1的話在遞推的過程中i從2開始遍歷本題就能過,然后就往結果上靠去解釋dp[0] = 1,從dp數組定義的角度上來說,dp[0] = 0 也能說得通,需要注意的是:題目中說了n是一個正整數,題目根本就沒說n有為0的情況,所以本題其實就不應該討論dp[0]的初始化!我相信dp[1] = 1,dp[2] = 2,這個初始化大家應該都沒有爭議的,所以我的原則是:不考慮dp[0]如何初始化,只初始化dp[1] = 1,dp[2] = 2,然后從i = 3開始遞推,這樣才符合dp[i]的定義
4.確定遍歷順序
從遞推公式dp[i] = dp[i - 1] + dp[i - 2];中可以看出,遍歷順序一定是從前向后遍歷的
5.舉例推導dp數組
舉例當n為5的時候,dp table(dp數組)應該是這樣的
在這里插入圖片描述
如果代碼出問題了,就把dp table 打印出來,看看究竟是不是和自己推導的一樣,此時大家應該發現了,這不就是斐波那契數列么!唯一的區別是,沒有討論dp[0]應該是什么,因為dp[0]在本題沒有意義

class Solution {
public:int climbStairs(int n) {if (n <= 1) return n; // 因為下面直接對dp[2]操作了,防止空指針vector<int> dp(n + 1);dp[1] = 1;dp[2] = 2;for (int i = 3; i <= n; i++) { // 注意i是從3開始的dp[i] = dp[i - 1] + dp[i - 2];}return dp[n];}
};

746. 使用最小花費爬樓梯

題目鏈接
視頻講解
給你一個整數數組 cost ,其中 cost[i] 是從樓梯第 i 個臺階向上爬需要支付的費用,一旦你支付此費用,即可選擇向上爬一個或者兩個臺階,你可以選擇從下標為 0 或下標為 1 的臺階開始爬樓梯,請你計算并返回達到樓梯頂部的最低花費

輸入:cost = [1,100,1,1,1,100,1,1,100,1]
輸出:6

1.確定dp數組以及下標的含義
使用動態規劃,就要有一個數組來記錄狀態,本題只需要一個一維數組dp[i]就可以了,dp[i]的定義:到達第i臺階所花費的最少體力為dp[i],對于dp數組的定義,一定要清晰!
2.確定遞推公式
可以有兩個途徑得到dp[i],一個是dp[i-1] 一個是dp[i-2],dp[i - 1] 跳到 dp[i] 需要花費 dp[i - 1] + cost[i - 1],dp[i - 2] 跳到 dp[i] 需要花費 dp[i - 2] + cost[i - 2],那么究竟是選從dp[i - 1]跳還是從dp[i - 2]跳呢?一定是選最小的,所以dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]);
3.dp數組如何初始化
看一下遞歸公式,dp[i]由dp[i - 1],dp[i - 2]推出,既然初始化所有的dp[i]是不可能的,那么只初始化dp[0]和dp[1]就夠了,其他的最終都是dp[0]dp[1]推出,那么 dp[0] 應該是多少呢? 根據dp數組的定義,到達第0臺階所花費的最小體力為dp[0],那么有同學可能想,那dp[0] 應該是 cost[0],例如 cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1] 的話,dp[0] 就是 cost[0] 應該是1,這里就要說明本題力扣為什么改題意,而且修改題意之后 就清晰很多的原因了,新題目描述中明確說了 “你可以選擇從下標為 0 或下標為 1 的臺階開始爬樓梯。” 也就是說 到達 第 0 個臺階是不花費的,但從 第0 個臺階 往上跳的話,需要花費 cost[0],所以初始化 dp[0] = 0,dp[1] = 0;
4.確定遍歷順序
最后一步,遞歸公式有了,初始化有了,如何遍歷呢?本題的遍歷順序其實比較簡單,簡單到都忽略了思考這一步直接就把代碼寫出來了,因為是模擬臺階,而且dp[i]由dp[i-1]dp[i-2]推出,所以是從前到后遍歷cost數組就可以了,但是稍稍有點難度的動態規劃,其遍歷順序并不容易確定下來。 例如:01背包,都知道兩個for循環,一個for遍歷物品嵌套一個for遍歷背包容量,那么為什么不是一個for遍歷背包容量嵌套一個for遍歷物品呢? 以及在使用一維dp數組的時候遍歷背包容量為什么要倒序呢?這些都與遍歷順序息息相關
5.舉例推導dp數組
拿示例2:cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1] ,來模擬一下dp數組的狀態變化,如下
在這里插入圖片描述

class Solution {
public:int minCostClimbingStairs(vector<int>& cost) {vector<int> dp(cost.size() + 1);dp[0] = 0; // 默認第一步都是不花費體力的dp[1] = 0;for (int i = 2; i <= cost.size(); i++) {dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]);}return dp[cost.size()];}
};

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

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

相關文章

計蒜客T1170——人民幣支付

超級水&#xff0c;不解釋&#xff0c;代碼的處理方式減低了繁瑣程度&#xff0c; #include <iostream> using namespace std;int main(int argc, char** argv) {int num0;cin>>num;int money[6]{100,50,20,10,5,1};for(int i0;i<5;i){int count0;countnum/mone…

SkyWalking 部署(包含ES)

SkyWalking安裝 結構 首先SkyWalking主要需要oapService、webApp、Elasticsearch&#xff08;可選存儲&#xff09;三個&#xff0c;接下來講一下這三個的安裝步驟&#xff0c;安裝過程中出現了一些細小的配置錯誤&#xff0c;導致用了快兩天才弄好&#xff0c;麻木了&#x…

C++超基礎語法

&#x1f493;博主個人主頁:不是笨小孩&#x1f440; ?專欄分類:數據結構與算法&#x1f440; C&#x1f440; 刷題專欄&#x1f440; C語言&#x1f440; &#x1f69a;代碼倉庫:笨小孩的代碼庫&#x1f440; ?社區&#xff1a;不是笨小孩&#x1f440; &#x1f339;歡迎大…

IDEA常用工具配置

IDEA常用工具&配置 如果發現插件市場用不了&#xff0c;可以設置Http Proxy&#xff0c;在該界面上點擊”Check connection“并輸入的地址&#xff1a;https://plugins.jetbrains.com/ 。 一、常用插件 1、MybatisX Mybaits Plus插件&#xff0c;支持java與xml互轉 2、F…

Vue-10.集成.env

.env、.env.development 和 .env.preview .env、.env.development 和 .env.preview 文件是用于配置環境變量和應用程序設置的文件&#xff0c;它們在項目開發和部署過程中起到關鍵作用。這些文件用于在不同的環境中設置不同的變量值&#xff0c;以滿足不同環境下的配置需求。 …

日志系統——日志格式化模塊設計

一&#xff0c;模塊主要成員 該模塊的主要作用是對日志消息進行格式化&#xff0c;將日志消息組織成制定格式的字符串。 該模塊主要成員有兩個&#xff1a;1.格式化字符串。 2.格式化子項數組 1.1 格式化字符串 格式化字符串的主要功能是保存日志輸出的格式字符串。其格式化字…

WPF 界面結構化處理

文章目錄 概要一、xaml界面結構化處理二、邏輯樹與視覺樹 概要 WPF 框架是開源的&#xff0c;但是不能跨平臺&#xff0c;可以使用MAUI&#xff0c;這個框架可以跨平臺&#xff0c;WPF源碼可以在github上下載&#xff0c;下載地址&#xff1a;https://gitbub.com/dotnet/wpf。…

【C++ 記憶站】命名空間

文章目錄 命名空間概念命名空間的定義1、正常的命名空間定義2、命名空間可以嵌套3、同一個工程中允許存在多個相同名稱的命名空間,編譯器最后會合成同一個命名空間中 命名空間的使用1、加命名空間名稱及作用域限定符2、使用using將命名空間中某個成員引入3、使用using namespac…

初試時間官宣!研招網發布下半年重要時間節點!今日速報來了

距24考研初試還有127天&#xff0c;今天給大家帶來初試和報名時間官宣消息、考研報名注意事項、研招網發布的2024考研“保姆級”下半年重要時間節點。有用記得收藏 24考研報名和初試時間官宣 已有學校在招生簡章中明確24考研初試時間 初試時間預計為&#xff1a;2023年12月23…

初試rabbitmq

rabbitmq的七種模式 Hello word 客戶端引入依賴 <!--rabbitmq 依賴客戶端--><dependency><groupId>com.rabbitmq</groupId><artifactId>amqp-client</artifactId><version>5.8.0</version></dependency> 生產者 imp…

邀請函|澎峰科技邀您參加CCF HPC China2023

一年一度的全球超算盛會&#xff01; 以“算力互聯智領未來”為主題的第十九屆全國高性能計算學術年會&#xff08;CCF HPC China 2023&#xff09;將于8月24-26日&#xff08;展覽23-25日&#xff09;在青島紅島國際會議展覽中心舉辦。 九大院士領銜 打造頂級超算盛會 力邀…

《離散數學及其應用(原書第8版)》ISBN978-7-111-63687-8 第11章 11.1.3 樹的性質 節 第664頁的例9說明

《離散數學及其應用&#xff08;原書第8版&#xff09;》ISBN978-7-111-63687-8 第11章 11.1.3 樹的性質 節 第664頁的定理3的引申 定理3 帶有i個內點的m叉樹含有nmi1個頂點 見本人博文 內點定義不同的討論 如果對于一個m叉正則樹&#xff0c;即任意分支節點的兒子恰好有m個&am…

談談IP地址和子網掩碼的概念及應用

個人主頁&#xff1a;insist--個人主頁?????? 本文專欄&#xff1a;網絡基礎——帶你走進網絡世界 本專欄會持續更新網絡基礎知識&#xff0c;希望大家多多支持&#xff0c;讓我們一起探索這個神奇而廣闊的網絡世界。 目錄 一、IP地址的概念 二、IP地址的分類 1、A類 …

長勝證券:散戶可以隨大流嗎?怎么做才好?

在我國的股市里邊&#xff0c;最不缺的或許便是散戶了&#xff0c;一方面&#xff0c;散戶促進了股市的活潑&#xff0c;可一方面又特容易望風而動&#xff0c;追漲殺跌。因此&#xff0c;散戶能夠隨大流嗎&#xff1f;該怎么做才好&#xff1f;對于這些&#xff0c;長勝證券為…

IntelliJ IDEA熱部署:JRebel插件的安裝與使用

熱部署 概述JRebel 概述 熱部署&#xff0c;指修改代碼后&#xff0c;無需停止應用程序&#xff0c;即可使修改后的代碼生效&#xff0c;其有利于提高開發效率。 熱部署方式&#xff1a; 手動熱部署&#xff1a;修改代碼后&#xff0c;重新編譯項目&#xff0c;然后啟動應用程…

Springboot項目啟動后按順序加載自定義類 (demo)

1. 實現ApplicationRunner接口, 重寫run方法 import lombok.extern.slf4j.Slf4j; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.core.annotation.Order; import org.springframewor…

IDEA啟動報錯java.nio.charset.MalformedInputException: Input length=2

IDEA啟動報錯java.nio.charset.MalformedInputException: Input length2 問題解決后記 問題 原本系統運行好好得&#xff0c;一段時間沒打開&#xff0c;再次打開重啟 IDEA啟動報錯java.nio.charset.MalformedInputException: Input length2。 解決 百度了 https://blog.csd…

使用 Qt 生成 Word 和 PDF 文檔的詳細教程

系列文章目錄 文章目錄 系列文章目錄前言一、安裝 Qt二、生成 Word 文檔三、生成 PDF 文檔四、運行代碼并查看結果五、自定義文檔內容總結前言 Qt 是一個跨平臺的應用程序開發框架,除了用于創建圖形界面應用程序外,還可以用來生成 Word 和 PDF 文檔。本文將介紹如何使用 Qt …

【C語言】const修飾普通變量和指針

大家好&#xff0c;我是蘇貝&#xff0c;本篇博客帶大家了解const修飾普通變量和指針&#xff0c;如果你覺得我寫的還不錯的話&#xff0c;可以給我一個贊&#x1f44d;嗎&#xff0c;感謝?? 文章目錄 一.const修飾普通變量二.const修飾指針1.const 放在 * 左邊2.const 放在…

git commit用法

git commit 是 Git 版本控制系統中的一個命令&#xff0c;用于將更改提交到本地存儲庫。以下是 git commit 的一些常見用法和選項&#xff1a; 基本用法: git commit -m "提交信息"使用 -m 選項可以直接在命令行中添加提交信息。 提交所有更改: git commit -a -m &q…