演算法之DP——買賣股票

it_was發表於2020-10-12

一直對動態規劃的題有種恐懼,每當做這類題的時候,總是感覺無從下手,思考一會就放棄了,然後去評論區貫徹我的拿來主義,到頭來還是沒真正搞懂。沒辦法,戰勝恐懼的最好辦法就是面對恐懼。目前只能用最淺顯的理解去解答這類題目,包括之前的秋葉收藏集,又何嘗不是呢?

給定一個陣列,它的第 i 個元素是一支給定股票第 i 天的價格。如果你最多隻允許完成一筆交易(即買入和賣出一支股票一次),設計一個演算法來計算你所能獲取的最大利潤。注意:你不能在買入股票前賣出股票。

思路

首先,最直觀的理解:當遍歷整個陣列的時候,每到一個位置,就去判斷一下當前股票如果賣出的話的利潤,然後更新最大值。而為了保持利潤的最大值,應該記錄到當前i位置的最小值(不包括當前 i ),以此作為買入。
程式碼如下:

public int maxProfit(int[] prices) {
        if (prices.length <= 1) {
            return 0;
        }
        int min = prices[0]; //初始化為陣列首元素
        int max = 0;
        for (int i = 1; i < prices.length; i++) {
            max = Math.max(max, prices[i] - min); //如果當前賣出
            min = Math.min(min, prices[i]);       //更新最小值
        }
        return max;
    }

難度簡單

給定一個陣列,它的第 i 個元素是一支給定股票第 i 天的價格。
設計一個演算法來計算你所能獲取的最大利潤。你可以儘可能地完成更多的交易(多次買賣一支股票)。
注意:你不能同時參與多筆交易(你必須在再次購買前出售掉之前的股票)。

思路

採用動態規劃,每當到一個 i 位置的時候無非就兩種狀態——持有股票和不持有股票,所以每一個狀態都可以由上一狀態決定
程式碼如下:

public int maxProfit(int[] prices) {
        if (prices.length <= 1) {
            return 0;
        }
        int len = prices.length;
        int[][] dp = new int[len][2]; //dp[i][0] 表示第i天不持有,dp[i][1]表示第i天持有
        dp[0][0] = 0;
        dp[0][1] = -prices[0];
        for (int i = 1; i < len; i++) {
            dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
            dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
        }
        return dp[len - 1][0];
    }

給定一個陣列,它的第 i 個元素是一支給定的股票在第 i 天的價格。
設計一個演算法來計算你所能獲取的最大利潤。你最多可以完成 兩筆 交易。
注意: 你不能同時參與多筆交易(你必須在再次購買前出售掉之前的股票)。

思路

由於限制了交易次數,故不能向上一題一樣無限次交易,此時需要記錄交易次數這個狀態。
程式碼如下:

 public int maxProfit(int[] prices) {
        if (prices.length <= 1) {
            return 0;
        }
        int k = 2;
        int[][][] dp = new int[prices.length][k + 1][2];
        for (int i = 0; i < prices.length; i++) {
            for (int j = k; j > 0; j--) {
                if (i == 0) {
                    //第i天,還有j次,手裡沒有股票,當i=0,手裡沒股票,最大利潤為0
                    dp[0][j][0] = 0;
                    dp[0][j][1] = -prices[0];
                    continue;
                }
                //今天手裡沒股票,比較MAX(前一天可能沒股票,前一天有股票但是今天賣出去了,賣出去就有利潤,所以+ prices[i])
                dp[i][j][0] = Math.max(dp[i - 1][j][0], dp[i - 1][j][1] + prices[i]);
                //今天手裡有股票,比較MAX(前一天可能有股票,前一天沒股票但是今天買了,買了就有成本,所以- prices[i])
                dp[i][j][1] = Math.max(dp[i - 1][j][1], dp[i - 1][j - 1][0] - prices[i]);
            }
        }
        return dp[prices.length - 1][k][0];
    }

給定一個陣列,它的第 i 個元素是一支給定的股票在第 i 天的價格。
設計一個演算法來計算你所能獲取的最大利潤。你最多可以完成 k 筆交易。
注意: 你不能同時參與多筆交易(你必須在再次購買前出售掉之前的股票)。

思路

此題同上,只不過需要判斷一下k的大小,進而轉換為無限次交易或者交易指定次數

 public int maxProfit(int k, int[] prices) {
        if (prices.length <= 1) {
            return 0;
        }
        int n = prices.length;
        //當k非常大時轉為無限次交易
        if(k>=n/2) {
            int dp0=0,dp1=-prices[0];
            for(int i=1;i<n;++i) {
                int tmp = dp0;
                dp0 = Math.max(dp0,dp1+prices[i]);
                dp1 = Math.max(dp1,dp0-prices[i]);
            }
            return Math.max(dp0,dp1);
        }
        int[][][] dp = new int[prices.length][k + 1][2];
        for (int i = 0; i < prices.length; i++) {
            for (int j = k; j > 0; j--) {
                if (i == 0) {
                    //第i天,還有j次,手裡沒有股票,當i=0,手裡沒股票,最大利潤為0
                    dp[0][j][0] = 0;
                    dp[0][j][1] = -prices[0];
                    continue;
                }
                //今天手裡沒股票,比較MAX(前一天可能沒股票,前一天有股票但是今天賣出去了,賣出去就有利潤,所以+ prices[i])
                dp[i][j][0] = Math.max(dp[i - 1][j][0], dp[i - 1][j][1] + prices[i]);
                //今天手裡有股票,比較MAX(前一天可能有股票,前一天沒股票但是今天買了,買了就有成本,所以- prices[i])
                dp[i][j][1] = Math.max(dp[i - 1][j][1], dp[i - 1][j - 1][0] - prices[i]);
            }
        }
        return dp[prices.length - 1][k][0];
    }
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章