快速上手leetcode動態規劃題

快速上手leetcode動態規劃題

我如今是初學的狀態,在此來記錄個人刷題過程,便於之後複習鞏固。java

我leetcode從動態規劃開始刷,語言用的java。數組

一.瞭解動態規劃

我上網查了一下動態規劃,瞭解到動態規劃是「帶有備忘錄的遞歸」,優化

而大多數用來理解動態規劃的例子都是斐波那契數列,就是那個經典的遞歸式spa

f(i)=f(i-1)+f(i-2) ,f(1)=f(2)=1code

那麼咱們就能夠獲得不少式子,好比求f(5):blog

f(5)=f(4)+f(3);遞歸

f(4)=f(3)+f(2);索引

f(3)=f(2)+f(1);leetcode

而後咱們就發現了重複的部分,在求f(5)和f(4)的時候都要求f(3),那麼它們都要作一次f(3)的遞歸操做,來獲得f(3)的值。get

我想這是很不值得的,不必一樣的操做執行兩遍。而且咱們知道當f(n)的n比較大時,是不少重複的部分的,這也就意味着有很大的優化空間。

所以有了所謂的「備忘錄」,也就是用一個數組來記錄每一個狀態的結果,好比f(5)就是n爲5時f(n)的狀態。

這樣的話,咱們就能夠在求f(n)的時候,先查看一下數組中是否記錄了這一個狀態的值,若是有,就直接從數組中拿,若是沒有,就遞歸計算一下,再把這個值放到數組中去。這也是所謂的「以空間換時間」的思想。

int[] dp=new int[n+1];//dp[i]表示f(i)的值

在求f(x)時:

if(dp[x]==0)//未被記錄到數組

dp[x]=f(x-1)+f(x-2)

return dp[x];

同時,遞歸也是會花費不少時間的,咱們可否換一種方式呢?

這時候咱們發現f(n)的狀態之間存在遞推關係,也就是f(n)=f(n-1)+f(n-2)

那麼這就對應了動態規劃的第二個關鍵因素狀態轉移方程,咱們把遞推關係轉化成數組dp先後的關係,

好比斐波拉契數列的就是dp[i]=dp[i-1]+dp[i-2]

有了這個方程,咱們就能夠循環求dp[i]的值了

dp[5]=dp[4]+dp[3],

dp[4]=dp[3]+dp[2],

dp[3]=dp[2]+dp[1];

那麼在求dp[5]的時候dp[4]和dp[3]已是保存在數組了,即可以直接得到。

咱們知道遞推和遞歸同樣,須要出口,也就是遞歸或遞推到底的標誌

在這道題中出口就是dp[1]=dp[2]=1;

有了這兩個值,在循環的時候咱們就能夠求出全部的值了,這就是出口的意義。

感受能夠類比數學裏數學概括法

//須要先作個判斷

if(n==1||n==2)

return 1;

dp[1]=dp[2]=1;

for(int i=3;i<n+1;i++)

{

dp[i]=dp[i-1]+dp[i-2];

}

return dp[n];

最後,我以爲重要的就是把握總體的邊界狀況,好比這裏的n==1和n==2是不用遞推關係的,並且dp[1]=dp[2]=1以前須要肯定n>2才能賦值的,有的題目裏還有給出參數爲一個數組,這時須要考慮數組長度爲0的狀況等等

最後總結一下動態規劃的四個要素(本身總結的):

1.定義數組

2.找出遞推關係

3.找出出口

4.把握總體邊界

它們在程序中的位置是4->1->3->2

最後返回值

2、刷題練習

70. 爬樓梯

假設你正在爬樓梯。須要 n 階你才能到達樓頂。

每次你能夠爬 1 或 2 個臺階。你有多少種不一樣的方法能夠爬到樓頂呢?

注意:給定 n 是一個正整數。

示例 1:

輸入: 2
輸出: 2
解釋: 有兩種方法能夠爬到樓頂。

  1. 1 階 + 1 階

  2. 2 階

示例 2:

輸入: 3
輸出: 3
解釋: 有三種方法能夠爬到樓頂。

  1. 1 階 + 1 階 + 1 階

  2. 1 階 + 2 階

  3. 2 階 + 1 階

解答:

思路

1.設置數組

經過題目,咱們知道咱們最終要求的是到達n階有多少種方法,那不妨就設這個爲dp[n]

那咱們要求的數組dp[i]表示的就是到達i階時有dp[i]種方法

2.找出遞推關係:

那咱們就要了解dp[i]是怎麼來的了?

根據題目,咱們知道,每次是能夠跨一階或者兩階的,那麼dp[i]就只有兩種方法獲得

一種是由dp[i-2]跨兩階來的,還有一種是dp[i-1]跨一階來的。

那麼dp[i]的方法數應該等於dp[i-1]的加上dp[i-2]了。

所以,咱們找出了遞推關係dp[i]=dp[i-1]+dp[i-2]

3.找出出口

根據遞推式咱們知道i>=2才能使用遞推獲得,否則下標就要<0了

那咱們求一下出口dp[0]=0,0階的時候確定只有0種方法

dp[1]=1;1階的時候只有跨1階這一種方法。

可是這裏還有一個dp[2]也是出口,可能被忽略掉,由於按照遞推式dp[2]=dp[1]+dp[0]=1,而實際上dp[2]=2

4.把握總體邊界:

n<=0,n==1,和n==2能夠提早算出

代碼

class Solution {

  public int climbStairs(int n) {

    //1.考慮總體邊界

    if(n<=0)

    return 0;

    if(n==1||n==2)

    return n;

 

    //2.設置數組

    int []dp=new int[n+1];//dp[i]表示到達i階,有dp[i]種方法

 

    //3.考慮數組邊界值

    dp[0]=0;

    dp[1]=1;

    dp[2]=2;//注意2也是邊界

 

    //4.找出dp[i]與dp[i-1]的關係,循環獲取所要得到的項dp[n];

    //dp[i]=dp[i-1]+dp[i-2]

    //要到達n階能夠有兩種方法:一種是從i-1爬1階來的,還有一種是i-2爬2階來的

    //所以須要求這兩種方法之和

    for(int i=3;i<=n;i++)

    {

      dp[i]=dp[i-1]+dp[i-2];

    }

    return dp[n];

  }

}

 

746. 使用最小花費爬樓梯

數組的每一個索引作爲一個階梯,第 i個階梯對應着一個非負數的體力花費值 costi

每當你爬上一個階梯你都要花費對應的體力花費值,而後你能夠選擇繼續爬一個階梯或者爬兩個階梯。

您須要找到達到樓層頂部的最低花費。在開始時,你能夠選擇從索引爲 0 或 1 的元素做爲初始階梯。

示例 1:

輸入: cost = [10, 15, 20]
輸出: 15
解釋: 最低花費是從cost[1]開始,而後走兩步便可到階梯頂,一共花費15。

示例 2:

輸入: cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1]
輸出: 6
解釋: 最低花費方式是從cost[0]開始,逐個通過那些1,跳過cost[3],一共花費6。

注意:

cost 的長度將會在 [2, 1000]。
每個 cost[i] 將會是一個Integer類型,範圍爲 [0, 999]

解答:

思路

1.設置數組

經過題目,咱們知道咱們最終要求的是到達n階的最低花費,那不妨就設這個爲dp[n]

那咱們要求的數組dp[i]表示的就是到達i階時的最低花費,根據示例,咱們可知最後要返回的結果應該是dp[len]

2.找出遞推關係:

首先咱們分析題目可知,消耗的體力值應該等於原來的加上到達的那一階的體力值,所以若是跨兩階的話,應該是直接加上兩階中的後一階的體力值的。

由於一次只能跨一階或者兩階,所以dp[i]應該是dp[i-2]和dp[i-1]中比較小的那個加上i對應的體力值,即cost[i];

所以dp[i]=Math.min(dp[i-2],dp[i-1])+cost[i]

3.找出出口

根據遞推式下標咱們知道i>1才能使用遞推式

那麼就須要求出dp[0]和dp[1]

dp[0]=cost[0];//到達0階時的花費,只有一個

dp[1]=cost[1];//到達1階一種是一階一階上,即cost[0]+cost[1],還有一種是直接上兩階cost[1],cost[1]更小

4.把握總體邊界:

參數是一個cost數組,咱們須要考慮數組長度爲0的狀況,遞推式不覆蓋下標爲0和1的,所以也應該拿出來做爲出口

    if(len==0)

    return 0;

    if(len==1)

    return cost[0];

    if(len==2)

    return cost[1];

代碼

class Solution {

  public int minCostClimbingStairs(int[] cost) {

    //設置出口

    int len=cost.length;

    if(len==0)

    return 0;

    if(len==1)

    return cost[0];

    if(len==2)

    return cost[1];

 

    //設置數組

    int []dp=new int[len];//dp[i]表示到達第i階時所花費的最小體力值

``   

    //設置數組邊界

    dp[0]=cost[0];

    dp[1]=cost[1];

 

    //找出數組的遞推關係

    int i;

    for(i=2;i<len;i++)

    {

      dp[i]=Math.min(dp[i-2],dp[i-1])+cost[i];

    }

 

    //返回值

    return Math.min(dp[i-2],dp[i-1]);

  }

}

相關文章
相關標籤/搜索