「程式碼隨想錄演算法訓練營」第三十五天 | 動態規劃 part8

云雀AC了一整天發表於2024-08-12

121. 買賣股票的最佳時機

題目連結:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock/
文章講解:https://programmercarl.com/0121.買賣股票的最佳時機.html
題目難度:簡單
影片講解:https://www.bilibili.com/video/BV1Xe4y1u77q
題目狀態:有一半的思路

思路一:貪心

對陣列進行遍歷,分別儲存兩個變數:最小的買入時間left,最大的獲利金額ans,遍歷完就得到答案了。

程式碼:

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int left = INT_MAX;
        int ans = 0;
        for(int i = 0; i < prices.size(); ++i) {
            left = min(left, prices[i]);
            ans = max(ans, prices[i] - left);
        }
        return ans;
    }
};

思路二:動規

建立一個二維動規陣列dp[i][],其大小為vector<len, vector<int>(2)>

  • dp[i][0]表示在第i天持有所花費的錢,此時並不一定是在i天買入的,也可能是在i-1天買入的,這個就是其動規的公式:dp[i][0] = max(dp[i - 1][0], -price[i])
  • dp[i][1]表示在第i天整到的最多的錢,此時也並不一定必須在i天賣出,也可能是在i-1天賣出的,取決於誰獲利最多,動規公式如下:dp[i][1] = max(price[i] + dp[i - 1][0], dp[i - 1][0])

image

程式碼:

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int len = prices.size();
        if(len == 0) return 0;
        vector<vector<int>> dp(len, vector<int>(2));
        dp[0][0] = -prices[0];
        dp[0][1] = 0;
        for(int i = 1; i < len; ++i) {
            dp[i][0] = max(dp[i - 1][0], -prices[i]);
            dp[i][1] = max(prices[i] + dp[i - 1][0], dp[i - 1][1]);
        }
        return dp[len - 1][1];
    }
};

思路二程式碼改進:

由於dp[i][]只有dp[i - 1][]來進行動規,因此只需要維持這兩個數就行,不需要維護其他數,採用滑動視窗的形式,程式碼如下:

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int len = prices.size();
        vector<vector<int>> dp(2, vector<int>(2)); // 注意這裡只開闢了一個2 * 2大小的二維陣列
        dp[0][0] -= prices[0];
        dp[0][1] = 0;
        for (int i = 1; i < len; i++) {
            dp[i % 2][0] = max(dp[(i - 1) % 2][0], -prices[i]);
            dp[i % 2][1] = max(dp[(i - 1) % 2][1], prices[i] + dp[(i - 1) % 2][0]);
        }
        return dp[(len - 1) % 2][1];
    }
};

122. 買賣股票的最佳時機 II

題目連結:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii/
文章講解:https://programmercarl.com/0122.買賣股票的最佳時機II(動態規劃).html
題目難度:中等
影片講解:https://www.bilibili.com/video/BV1D24y1Q7Ls
題目狀態:有一半的思路

思路一:貪心

這題在學習貪心演算法的時候做過,只需要遍歷陣列,然後將具有正收益的盈利金額加起來。

程式碼:

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int res = 0;
        for(int i = 1; i < prices.size(); ++i) {
            res += max(prices[i] - prices[i - 1], 0);
        }
        return res;
    }
};

思路二:動態規劃

這裡和上一題一樣,也要維持一個二維動規陣列,但有一個地方需要改變,就是在dp[i][0]的計算上,上一題因為只能進行一次交易,所以dp[i][0]要麼是dp[i - 1][0](在第i天之前已經進行買入),要麼是-price[i](在當天買入)。但這道題中,我們可以進行多次交易,因此dp[i][0]要麼是dp[i - 1][0](在第i天之前進行買入),要麼是dp[i - 1][1] - price[i](在當天進行買入,我們要將之前的收益也要加進來)。
要始終記得:dp[i][0]表示第i天持有股票的最大利潤;dp[i][1]表示第i天不持有股票的最大利潤

程式碼:

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int len = prices.size();
        if(len == 0) return 0;
        vector<vector<int>> dp(len, vector<int>(2));
        dp[0][0] = -prices[0];
        dp[0][1] = 0;
        for(int i = 1; i < len; ++i) {
            dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]);
            dp[i][1] = max(dp[i - 1][0] + prices[i], dp[i - 1][1]);
        }
        return dp[len - 1][1];
    }
};

滾動陣列版本:

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int len = prices.size();
        vector<vector<int>> dp(2, vector<int>(2)); // 注意這裡只開闢了一個2 * 2大小的二維陣列
        dp[0][0] -= prices[0];
        dp[0][1] = 0;
        for (int i = 1; i < len; i++) {
            dp[i % 2][0] = max(dp[(i - 1) % 2][0], dp[(i - 1) % 2][1] - prices[i]);
            dp[i % 2][1] = max(dp[(i - 1) % 2][1], prices[i] + dp[(i - 1) % 2][0]);
        }
        return dp[(len - 1) % 2][1];
    }
};

123. 買賣股票的最佳時機 III

題目連結:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iii/
文章講解:https://programmercarl.com/0123.買賣股票的最佳時機III.html
題目難度:困難
影片講解:https://www.bilibili.com/video/BV1WG411K7AR
題目狀態:沒思路

思路:

這次的動規陣列主要維護的是五個狀態:

  1. 無操作
  2. 第一次持有股票
  3. 第一次不持有股票
  4. 第二次持有股票
  5. 第二次不持有股票

dp[i][j]中i表示第i天,j就是上面五個狀態,dp[i][j]表示第i天狀態j的所剩最大現金。

因此:每個狀態就會有兩種操作:

  • 有操作:假設是狀態1,那麼就表示當天買入股票了,則dp[i][1] = dp[i - 1][0] - prices[i]
  • 沒操作:就是這天沒進行買入操作,那麼dp[i][1] = dp[i - 1][1]

其他狀態同上。

image

程式碼:

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int len = prices.size();
        if(len == 0) return 0;
        vector<vector<int>> dp(len, vector<int>(5));
        dp[0][1] = -prices[0];
        dp[0][3] = -prices[0];
        for(int i = 1; i < len; ++i) {
            dp[i][0] = dp[i - 1][0];
            dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
            dp[i][2] = max(dp[i - 1][2], dp[i - 1][1] + prices[i]);
            dp[i][3] = max(dp[i - 1][3], dp[i - 1][2] - prices[i]);
            dp[i][4] = max(dp[i - 1][4], dp[i - 1][3] + prices[i]);
        }
        return dp[len - 1][4];
    }
};

相關文章