Best Time To Buy And Sell Stock 買賣股票最佳時機

關鍵字:leetcode,Best Time To Buy And Sell Stock,算法,algorithm,動態規劃,dynamic programmingjava

leetcode 上關於Best Time to Buy and Sell Stock主題的題目有四個:算法

這四個題目難度依次遞增。大體意思就是,給咱們一個 List<Integer> prices ,而後讓咱們找到怎麼買賣才能得到最大收益。其中第四個問題是尋求一個通解,在給定 prices和最大買賣次數k的狀況下,求最大收益。數組

首先大體的解題方向是動態規劃,這個應該不難想到。以後就是怎麼找到狀態,怎麼列狀態轉移方程。考慮某一天的狀況,能夠有以下三種狀態:ui

  • 當天買入
  • 當天賣出
  • 當天什麼也沒作

由於這個題目咱們要找到最大收益,因此,若是最後一天的狀態是買入的話,那麼其收益必定不是最大的,由於最後一天買入的話,就沒有機會賣出了。那麼,上面的三個狀態能夠減小到兩個:code

  1. 當天賣出。
  2. 賣出,可是不在當天(即在前面的某一天)。

因此,咱們用 soldAtToday[k] 來表示當天賣出且賣出的時候交易了 k 手的時候的最大收益,soldNotAtToday[k] 表明在以前某天賣出,且當前交易手數爲 k 的時候的最大收益。那麼,狀態轉移方程就能夠用以下僞代碼描述:ip

soldAtToday[k] =  max(soldAtYesterday[k] + price, soldNotAtYesterday[k - 1] + price);
soldNotAtToday[k] = max(soldAtYesterday[k], soldNotAtYesterday[k]);

其中,k 是交易次數。須要注意的是soldAtToday[k] = max(soldAtYesterday[k] + price, soldNotAtYesterday[k - 1] + price);中第一個備選項是soldAtYesterday[k],而不是soldAtYesterday[k - 1],其含義就是,把本應該昨天賣的延長一天到今天賣,因此交易次數仍是 k 。leetcode

具體實現的時候,咱們發現 soldAtToday 和 soldNotAtToday 都是隻依賴於 soldAtYesterday 和 soldNotAtYesterday,因此咱們能夠利用兩個長度爲 k + 1 的數組來完成 today 和 yesterday 數據的存儲。get

package BestTimeToBuyAndSellStock.VersionIV;

@SuppressWarnings("Duplicates")
class Solution {

    private int quickSolve(int[] prices) {
        int len = prices.length, profit = 0;
        for (int i = 1; i < len; i++) {
            // as long as there is a price gap, we gain a profit.
            if (prices[i] > prices[i - 1]) profit += prices[i] - prices[i - 1];
        }
        return profit;
    }

    public int maxProfit(int k, int[] prices) {
        if (prices == null || prices.length <= 1 || k <= 0) {
            return 0;
        }

        int len = prices.length;

        if (k >= len / 2) return quickSolve(prices);


        int today = 1;
        int yesterday = 0;
        int[][] soldAt = new int[2][k + 1];
        int[][] soldNotAt = new int[2][k + 1];

        soldAt[today][0] = 0;
        soldAt[yesterday][0] = 0;
        soldNotAt[today][0] = 0;
        soldNotAt[yesterday][0] = 0;

        for (int i = 0; i < k + 1; i++) {
            soldAt[yesterday][i] = 0;
            soldNotAt[yesterday][i] = 0;
        }

        for (int i = 1; i < prices.length; i++) {
            int price = prices[i] - prices[i - 1];
            for (int j = 1; j < k + 1; j++) {
                soldAt[today][j] = Math.max(
                        soldAt[yesterday][j] + price,
                        soldNotAt[yesterday][j - 1] + price
                );
                soldNotAt[today][j] = Math.max(
                        soldAt[yesterday][j],
                        soldNotAt[yesterday][j]
                );
            }
            int tmp = yesterday;
            yesterday = today;
            today = tmp;
        }

        return Math.max(soldAt[yesterday][k], soldNotAt[yesterday][k]);
    }
}

觀察代碼發現,開頭多了一個 quickSolve 。這是由於,若是直接把上面咱們描述的算法實現出來,提交上去,會出現 TimeLimted 的問題。仔細分析了一下超時的用例,發現他們的特徵是 k 很大。其實,當 k 大於 prices.length / 2 的時候,就至關於沒有 k 的限制,即隨便買賣了,由於不考慮當天買當天賣的狀況,或者把當天買賣的收益虛擬的記爲 0 。那麼咱們的算法就退化成最簡單的狀況,因而使用一個 quickSolve 來解決便可。
這個題目也對咱們平常工做提供了啓示:出現問題了,找到瓶頸,分析特徵,而後突破。it

相關文章
相關標籤/搜索