自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

數(shù)據(jù)結(jié)構(gòu)與算法之使用最小花費(fèi)爬樓梯

開發(fā) 后端 算法
數(shù)組的每個(gè)下標(biāo)作為一個(gè)階梯,第 i 個(gè)階梯對(duì)應(yīng)著一個(gè)非負(fù)數(shù)的體力花費(fèi)值 cost[i](下標(biāo)從 0 開始)。

[[443068]]

使用最小花費(fèi)爬樓梯

力扣題目鏈接:https://leetcode-cn.com/problems/min-cost-climbing-stairs

數(shù)組的每個(gè)下標(biāo)作為一個(gè)階梯,第 i 個(gè)階梯對(duì)應(yīng)著一個(gè)非負(fù)數(shù)的體力花費(fèi)值 cost[i](下標(biāo)從 0 開始)。

每當(dāng)你爬上一個(gè)階梯你都要花費(fèi)對(duì)應(yīng)的體力值,一旦支付了相應(yīng)的體力值,你就可以選擇向上爬一個(gè)階梯或者爬兩個(gè)階梯。

請(qǐng)你找出達(dá)到樓層頂部的最低花費(fèi)。在開始時(shí),你可以選擇從下標(biāo)為 0 或 1 的元素作為初始階梯。

示例 1:

  • 輸入:cost = [10, 15, 20]
  • 輸出:15
  • 解釋:最低花費(fèi)是從 cost[1] 開始,然后走兩步即可到階梯頂,一共花費(fèi) 15 。

示例 2:

  • 輸入:cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1]
  • 輸出:6
  • 解釋:最低花費(fèi)方式是從 cost[0] 開始,逐個(gè)經(jīng)過那些 1 ,跳過 cost[3] ,一共花費(fèi) 6 。

提示:

  • cost 的長(zhǎng)度范圍是 [2, 1000]。
  • cost[i] 將會(huì)是一個(gè)整型數(shù)據(jù),范圍為 [0, 999] 。

思路

這道題目可以說是昨天動(dòng)態(tài)規(guī)劃:爬樓梯的花費(fèi)版本。

注意題目描述:每當(dāng)你爬上一個(gè)階梯你都要花費(fèi)對(duì)應(yīng)的體力值,一旦支付了相應(yīng)的體力值,你就可以選擇向上爬一個(gè)階梯或者爬兩個(gè)階梯

所以示例1中只花費(fèi)一個(gè)15 就可以到階梯頂,最后一步可以理解為 不用花費(fèi)。

讀完題大家應(yīng)該知道指定需要?jiǎng)討B(tài)規(guī)劃的,貪心是不可能了。

確定dp數(shù)組以及下標(biāo)的含義

使用動(dòng)態(tài)規(guī)劃,就要有一個(gè)數(shù)組來記錄狀態(tài),本題只需要一個(gè)一維數(shù)組dp[i]就可以了。

dp[i]的定義:到達(dá)第i個(gè)臺(tái)階所花費(fèi)的最少體力為dp[i]。(注意這里認(rèn)為是第一步一定是要花費(fèi))

對(duì)于dp數(shù)組的定義,大家一定要清晰!

確定遞推公式

可以有兩個(gè)途徑得到dp[i],一個(gè)是dp[i-1] 一個(gè)是dp[i-2]。

那么究竟是選dp[i-1]還是dp[i-2]呢?

一定是選最小的,所以dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i];

注意這里為什么是加cost[i],而不是cost[i-1],cost[i-2]之類的,因?yàn)轭}目中說了:每當(dāng)你爬上一個(gè)階梯你都要花費(fèi)對(duì)應(yīng)的體力值

dp數(shù)組如何初始化

根據(jù)dp數(shù)組的定義,dp數(shù)組初始化其實(shí)是比較難的,因?yàn)椴豢赡艹跏蓟癁榈趇臺(tái)階所花費(fèi)的最少體力。

那么看一下遞歸公式,dp[i]由dp[i-1],dp[i-2]推出,既然初始化所有的dp[i]是不可能的,那么只初始化dp[0]和dp[1]就夠了,其他的最終都是dp[0]dp[1]推出。

所以初始化代碼為:

  1. vector<int> dp(cost.size()); 
  2. dp[0] = cost[0]; 
  3. dp[1] = cost[1]; 

確定遍歷順序

最后一步,遞歸公式有了,初始化有了,如何遍歷呢?

本題的遍歷順序其實(shí)比較簡(jiǎn)單,簡(jiǎn)單到很多同學(xué)都忽略了思考這一步直接就把代碼寫出來了。

因?yàn)槭悄M臺(tái)階,而且dp[i]又dp[i-1]dp[i-2]推出,所以是從前到后遍歷cost數(shù)組就可以了。

但是稍稍有點(diǎn)難度的動(dòng)態(tài)規(guī)劃,其遍歷順序并不容易確定下來。

例如:01背包,都知道兩個(gè)for循環(huán),一個(gè)for遍歷物品嵌套一個(gè)for遍歷背包容量,那么為什么不是一個(gè)for遍歷背包容量嵌套一個(gè)for遍歷物品呢?以及在使用一維dp數(shù)組的時(shí)候遍歷背包容量為什么要倒敘呢?

這些都是遍歷順序息息相關(guān)。當(dāng)然背包問題后續(xù)「代碼隨想錄」都會(huì)重點(diǎn)講解的!

舉例推導(dǎo)dp數(shù)組

拿示例2:cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1] ,來模擬一下dp數(shù)組的狀態(tài)變化,如下:

使用最小花費(fèi)爬樓梯

如果大家代碼寫出來有問題,就把dp數(shù)組打印出來,看看和如上推導(dǎo)的是不是一樣的。

以上分析完畢,整體C++代碼如下:

  1. // 版本一 
  2. class Solution { 
  3. public
  4.     int minCostClimbingStairs(vector<int>& cost) { 
  5.         vector<int> dp(cost.size()); 
  6.         dp[0] = cost[0]; 
  7.         dp[1] = cost[1]; 
  8.         for (int i = 2; i < cost.size(); i++) { 
  9.             dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i]; 
  10.         } 
  11.         // 注意最后一步可以理解為不用花費(fèi),所以取倒數(shù)第一步,第二步的最少值 
  12.         return min(dp[cost.size() - 1], dp[cost.size() - 2]); 
  13.     } 
  14. }; 
  • 時(shí)間復(fù)雜度:
  • 空間復(fù)雜度:

還可以優(yōu)化空間復(fù)雜度,因?yàn)閐p[i]就是由前兩位推出來的,那么也不用dp數(shù)組了,C++代碼如下:

  1. // 版本二 
  2. class Solution { 
  3. public
  4.     int minCostClimbingStairs(vector<int>& cost) { 
  5.         int dp0 = cost[0]; 
  6.         int dp1 = cost[1]; 
  7.         for (int i = 2; i < cost.size(); i++) { 
  8.             int dpi = min(dp0, dp1) + cost[i]; 
  9.             dp0 = dp1; // 記錄一下前兩位 
  10.             dp1 = dpi; 
  11.         } 
  12.         return min(dp0, dp1); 
  13.     } 
  14. }; 
  • 時(shí)間復(fù)雜度:
  • 空間復(fù)雜度:

當(dāng)然我不建議這么寫,能寫出版本一就可以了,直觀簡(jiǎn)潔!

在后序的講解中,可能我會(huì)忽略這種版本二的寫法,大家只要知道有這么個(gè)寫法就可以了哈。

拓展

這道題描述也確實(shí)有點(diǎn)魔幻。

題目描述為:每當(dāng)你爬上一個(gè)階梯你都要花費(fèi)對(duì)應(yīng)的體力值,一旦支付了相應(yīng)的體力值,你就可以選擇向上爬一個(gè)階梯或者爬兩個(gè)階梯。

示例1:

輸入:cost = [10, 15, 20] 輸出:15

從題目描述可以看出:要不是第一步不需要花費(fèi)體力,要不就是第最后一步不需要花費(fèi)體力,我個(gè)人理解:題意說的其實(shí)是第一步是要支付費(fèi)用的!。因?yàn)槭钱?dāng)你爬上一個(gè)臺(tái)階就要花費(fèi)對(duì)應(yīng)的體力值!

所以我定義的dp[i]意思是也是第一步是要花費(fèi)體力的,最后一步不用花費(fèi)體力了,因?yàn)橐呀?jīng)支付了。

當(dāng)然也可以樣,定義dp[i]為:第一步是不花費(fèi)體力,最后一步是花費(fèi)體力的。

所以代碼這么寫:

  1. class Solution { 
  2. public
  3.     int minCostClimbingStairs(vector<int>& cost) { 
  4.         vector<int> dp(cost.size() + 1); 
  5.         dp[0] = 0; // 默認(rèn)第一步都是不花費(fèi)體力的 
  6.         dp[1] = 0; 
  7.         for (int i = 2; i <= cost.size(); i++) { 
  8.             dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]); 
  9.         } 
  10.         return dp[cost.size()]; 
  11.     } 
  12. }; 

這么寫看上去比較順,但是就是感覺和題目描述的不太符。哈哈,也沒有必要這么細(xì)扣題意了,大家只要知道,題目的意思反正就是要不是第一步不花費(fèi),要不是最后一步不花費(fèi),都可以。

總結(jié)

大家可以發(fā)現(xiàn)這道題目相對(duì)于 昨天的動(dòng)態(tài)規(guī)劃:爬樓梯有難了一點(diǎn),但整體思路是一樣。

從動(dòng)態(tài)規(guī)劃:斐波那契數(shù)到 動(dòng)態(tài)規(guī)劃:爬樓梯再到今天這道題目,錄友們感受到循序漸進(jìn)的梯度了嘛。

每個(gè)系列開始的時(shí)候,都有錄友和我反饋說題目太簡(jiǎn)單了,趕緊上難度,但也有錄友和我說有點(diǎn)難了,快跟不上了。

其實(shí)我選的題目都是有目的性的,就算是簡(jiǎn)單題,也是為了練習(xí)方法論,然后難度都是梯度上來的,一環(huán)扣一環(huán)。

但我也可以隨便選來一道難題講唄,這其實(shí)是最省事的,不用管什么題目順序,看心情找一道就講。

難的是把題目按梯度排好,循序漸進(jìn),再按照統(tǒng)一方法論把這些都串起來,哈哈,所以大家不要催我哈,按照我的節(jié)奏一步一步來就行啦。

學(xué)算法,認(rèn)準(zhǔn)「代碼隨想錄」,沒毛病!

其他語言版本

Java

  1. class Solution { 
  2.     public int minCostClimbingStairs(int[] cost) { 
  3.         if (cost == null || cost.length == 0) { 
  4.             return 0; 
  5.         } 
  6.         if (cost.length == 1) { 
  7.             return cost[0]; 
  8.         } 
  9.         int[] dp = new int[cost.length]; 
  10.         dp[0] = cost[0]; 
  11.         dp[1] = cost[1]; 
  12.         for (int i = 2; i < cost.length; i++) { 
  13.             dp[i] = Math.min(dp[i - 1], dp[i - 2]) + cost[i]; 
  14.         } 
  15.         //最后一步,如果是由倒數(shù)第二步爬,則最后一步的體力花費(fèi)可以不用算 
  16.         return Math.min(dp[cost.length - 1], dp[cost.length - 2]); 
  17.     } 

Python

  1. class Solution: 
  2.     def minCostClimbingStairs(self, cost: List[int]) -> int
  3.         dp = [0] * (len(cost)) 
  4.         dp[0] = cost[0] 
  5.         dp[1] = cost[1] 
  6.         for i in range(2, len(cost)): 
  7.             dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i] 
  8.         return min(dp[len(cost) - 1], dp[len(cost) - 2]) 

Go

  1. func minCostClimbingStairs(cost []intint { 
  2.  dp := make([]int, len(cost)) 
  3.  dp[0], dp[1] = cost[0], cost[1] 
  4.  for i := 2; i < len(cost); i++ { 
  5.   dp[i] = min(dp[i-1], dp[i-2]) + cost[i] 
  6.  } 
  7.  return min(dp[len(cost)-1], dp[len(cost)-2]) 
  8.  
  9. func min(a, b intint { 
  10.  if a < b { 
  11.   return a 
  12.  } 
  13.  return b 

 Javascript

  1. var minCostClimbingStairs = function(cost) { 
  2.     const dp = [ cost[0], cost[1] ] 
  3.  
  4.     for (let i = 2; i < cost.length; ++i) { 
  5.         dp[i] = Math.min(dp[i -1] + cost[i], dp[i - 2] + cost[i]) 
  6.     } 
  7.  
  8.     return Math.min(dp[cost.length - 1], dp[cost.length - 2]) 
  9. }; 

 

責(zé)任編輯:姜華 來源: 代碼隨想錄
相關(guān)推薦

2021-11-04 09:59:03

動(dòng)態(tài)規(guī)劃策略

2021-12-29 11:32:38

數(shù)據(jù)結(jié)構(gòu)算法爬樓梯

2020-10-30 09:56:59

Trie樹之美

2022-09-21 07:57:33

二叉搜索樹排序二叉樹

2022-09-26 07:56:53

AVL算法二叉樹

2020-12-31 05:31:01

數(shù)據(jù)結(jié)構(gòu)算法

2020-10-21 14:57:04

數(shù)據(jù)結(jié)構(gòu)算法圖形

2023-03-08 08:03:09

數(shù)據(jù)結(jié)構(gòu)算法歸并排序

2020-10-20 08:14:08

算法與數(shù)據(jù)結(jié)構(gòu)

2020-10-12 11:48:31

算法與數(shù)據(jù)結(jié)構(gòu)

2022-01-18 19:13:52

背包問題數(shù)據(jù)結(jié)構(gòu)算法

2023-10-27 07:04:20

2021-09-29 18:28:41

數(shù)據(jù)結(jié)構(gòu)算法最小生成樹

2021-12-08 11:31:43

數(shù)據(jù)結(jié)構(gòu)算法合并區(qū)間

2021-12-21 11:39:01

數(shù)據(jù)結(jié)構(gòu)算法同構(gòu)字符串

2021-12-10 11:27:59

數(shù)據(jù)結(jié)構(gòu)算法單調(diào)遞增的數(shù)字

2009-08-11 14:43:42

C#數(shù)據(jù)結(jié)構(gòu)與算法

2021-07-16 04:57:45

Go算法結(jié)構(gòu)

2009-08-11 14:51:11

C#數(shù)據(jù)結(jié)構(gòu)與算法

2023-03-07 08:02:07

數(shù)據(jù)結(jié)構(gòu)算法數(shù)列
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)