算法实践:leetcode123 买股票的最佳时机

买股票的最佳时机

描述

给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。

注意: 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

样例

输入: [3,3,5,0,0,3,1,4]
输出: 6
解释: 在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 。随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3 。
输入: [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。   
注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。   
因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。

输入: [7,6,4,3,1] 
输出: 0 
解释: 在这个情况下, 没有交易完成, 所以最大利润为 0。

难度

高,动态规划

解法

状态定义:f(x, y) -------- 第x笔交易在第y天能取得的最大利润

状态转移:

(1)当x == 1时 第一笔交易进行时

​ a:我们可以选择在第y天不卖出也不买入,

​ a-1:如果此时y == 0,即第0天,那么f(1, 0) = 0,即取得的最大利润为0。

​ a-2:如果此时y > 0,那么此时我们的第x笔交易在第y天能取得的最大利润是f(1, y - 1),因为我们在第y天既不买入也不卖出,取得的最大利润自然和第x笔交易在第y - 1天能取得的最大利润相同。

​ b:我们可以选择在第y天卖出,

​ b-1:如果此时y == 0,显然,我们不可能在第0天卖出,这种情况不予讨论。

​ b-2:如果此时y > 0,f(x, y) = max(prices[y] - prices[b]),其中0 <= b < y,代表我们在第b天买入。

综上,对于x == 1的情况,当y == 0时,f(1, 0) = 0;当y > 0时,f(1, y) = max(f(1, y - 1), prices[y] - prices[b]),0 <= b < y。

(2)当x == 2时

​ a:我们可以选择在第y天不卖出也不买入,

​ a-1:如果此时y == 0,即第0天,那么f(x, y) = 0,即取得的最大利润为0。

​ a-2:如果此时y > 0,那么此时我们的第x笔交易在第y天能取得的最大利润是f(2, y - 1),因为我们在第y天既不买入也不卖出,取得的最大利润自然和第x笔交易在第y - 1天能取得的最大利润相同。

​ b:我们可以选择在第y天卖出,

​ b-1:如果此时y == 0,显然,我们不可能在第0天卖出,这种情况不予讨论。

​ b-2:如果此时y > 0,f(x, y) = max(prices[y] - prices[b] , f(1, b - 1)),其中0 <= b < y,代表我们在第b天买入。

综上,对于x == 2的情况,当y == 0时,f(2, 0) = 0;当y > 0时,f(2, y) = max(f(2, y - 1), prices[y] - prices[b] + f(1, b - 1)),0 <= b < y。

时间复杂度是O(kn ^ 2),其中k为交易次数,n为prices数组的大小。空间复杂度是O(kn)。

例如:3,3,5,0,0,3,1,4

p 3 3 5 0 0 3 1 4
1 0 0 2 2 2 3 3 4
2 0 0 2 2 2 5 5 6

代码

class Solution {
    public int maxProfit(int[] prices) {
        int result = 0;
        if(0 == prices.length){
            return result;
        }
        int[][] dp = new int[2][prices.length];
        for(int k = 0; k < 2; k++){               //循环两次交易
            dp[k][0] = 0;
            int min = prices[0];
            for(int i = 1; i < prices.length; i++){    //i代表正进行第i笔交易
                for(int b = 1; b < i; b++){          //b代表买入时间
                    if(k == 0){                           //第一次交易时
                        min = Math.min(min, prices[b]);
                    }else{                             //第二次交易时
                        min = Math.min(min, prices[b] - dp[k - 1][b - 1]);
                    }
                }
                dp[k][i] = Math.max(dp[k][i - 1], prices[i] - min);            //比较不卖出和卖出的利润
            }
        }
        return dp[1][prices.length - 1];
    }
}
发布了130 篇原创文章 · 获赞 148 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/matafeiyanll/article/details/105377034