LeetCode算法練習——動態規劃入門(二)

LeetCode746. 使用最小花費爬樓梯

數組的每個索引作爲一個階梯,第 i個階梯對應着一個非負數的體力花費值 cost[i](索引從0開始)。每當你爬上一個階梯你都要花費對應的體力花費值,然後你可以選擇繼續爬一個階梯或者爬兩個階梯。您需要找到達到樓層頂部的最低花費。在開始時,你可以選擇從索引爲 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。

在看該題之前我們先來看下面這一小題:

LeetCode70. 爬樓梯 && 面試題10- II. 青蛙跳臺階問題

假設你正在爬樓梯。需要 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 階

此題我們很容易得到遞推式F(n) = F(n - 1) + F(n - 2)

class Solution {
public:
    int climbStairs(int n) {//f(n) = f(n - 1) + f(n - 2)
        vector<int> dp(n + 1);
        if(n < 2)  return n;
        else{
            dp[0] = 1;
            dp[1] = 1;
            for(int i = 2; i <= n; i++)
                dp[i] = dp[i - 1] + dp[i -2];
        }
        return dp[n];
    }
};

LeetCode 面試題 08.01. 三步問題

有個小孩正在上樓梯,樓梯有n階臺階,小孩一次可以上1階、2階或3階。實現一種方法,計算小孩有多少種上樓梯的方式。結果可能很大,你需要對結果模1000000007。

此題F(n) = F(n - 1) + F(n - 2) + F(n - 3),注意F(3)的初值不是3。

class Solution {
public:
    int waysToStep(int n) {
        if(n == 0)    return 0;
        else if(n == 1) return 1;
        else if(n == 2) return 2;
        else if(n == 3) return 4;//需要注意的是上三階臺階的特殊情況
        vector<int> dp(n+1);
        dp[0] = 0;
        dp[1] = 1;
        dp[2] = 2;
        dp[3] = 4;
        for(int i = 4; i <= n; i++)
            dp[i] = ((dp[i - 1]%1000000007 + dp[i - 2]%1000000007)%1000000007 + dp[i - 3]%1000000007)%1000000007;
        return dp[n];
    }
};

本題思路

本題是上一類題的變種,增加了爬樓梯代價開銷的計算,以及起點可以從索引0開始也可以從索引1開始的條件。

  • 由於起始點的條件發生了變化,因此初值設立:dp[0] = cost[0],dp[1] = cost[1],dp的容量爲cost數組的大小;
  • 既然是計算最小開銷,那麼我們需要計算最後一步完成前只走一步的情況和最後一步完成前只走兩步情況,比較其最小值,即確定了狀態方程dp[i] = min(dp[i - 1] + cost[i], dp[i - 2] + cost[i] );
  • 當計算完成時我們返回dp[length - 1], dp[length -2]中的最小值,這是因爲:當我們從索引0位置以1步或者2步的方式到達length-1的位置時,同樣可以從索引1位置開始以1步或者2步的方式到達length終點位置時,因此我們需要比較0~length-1位置和0~length位置的開銷值,取得最小值。
class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        int length = cost.size();
        vector<int> dp(length);
        dp[0] = cost[0];
        dp[1] = cost[1];
        for(int i = 2; i < length; i++){
            dp[i] = min(dp[i - 1] + cost[i], dp[i - 2] + cost[i] );
        }
        return min(dp[length - 1], dp[length -2]);
    }
};

LeetCode面試題 16.17. 連續數列

給定一個整數數組,找出總和最大的連續數列,並返回總和。

示例:

輸入: [-2,1,-3,4,-1,2,1,-5,4]
輸出: 6
解釋: 連續子數組 [4,-1,2,1] 的和最大,爲 6。

狀態: dp[i]表示以i結尾的最大連續子序列
狀態轉移:

  • 對於當前的nums[i],如果nums[i-1] >= 0 則 dp[i] = dp[i-1] + nums[i];
  • 否則 dp[i] = nums[i];

其實我們可以把nums當做dp數組,直接在原數組上面操作,這樣可以省掉O(n)的空間

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        if(nums.size() == 0) return INT_MIN; 
        int maxSum = nums[0];
        for(int i = 1; i < nums.size(); i++)
        {
            if(nums[i - 1] >= 0)
                nums[i] += nums[i - 1];
            maxSum = max(maxSum, nums[i]);
        }
        return maxSum;
    }
};

LeetCode1025. 除數博弈

愛麗絲和鮑勃一起玩遊戲,他們輪流行動。愛麗絲先手開局。

最初,黑板上有一個數字 N 。在每個玩家的回合,玩家需要執行以下操作:

  •     選出任一 x,滿足 0 < x < N 且 N % x == 0 。
  •     用 N - x 替換黑板上的數字 N 。

如果玩家無法執行這些操作,就會輸掉遊戲。只有在愛麗絲在遊戲中取得勝利時才返回 True,否則返回 false。假設兩個玩家都以最佳狀態參與遊戲。

示例 1:

輸入:2
輸出:true
解釋:愛麗絲選擇 1,鮑勃無法進行操作。

示例 2:

輸入:3
輸出:false
解釋:愛麗絲選擇 1,鮑勃也選擇 1,然後愛麗絲無法進行操作。

其實,在該題中,只要數爲偶數即可勝利,但題目的本意還是用動態規劃來解題,根據玩家執行操作很容易得到狀態轉移方程:N % x == 0 && f(N - x) == false,代碼如下:

class Solution {
public:
    bool divisorGame(int N) {       
        //能整除且對手無法操作
        //N % x == 0 && f(N - x) == false
        if (N < 2) return false;
        vector<bool> dp(N + 1, false);
        dp[2] = true;
        for(int i = 3; i <= N; i++){
            for(int j = 1; j < i; j++){
                if(i%j == 0 && dp[i - j] == false){
                    dp[i] = true;
                    break;
                }
            }
        }
        return dp[N];
    }
};

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章