股票交易问题全拿下,一扫光

上来直接最难的,后面都是毛毛雨

问题一:买卖股票的最佳时机IV(leetcode188)

问题二:最大可交易次数不限(leetcode122)

问题三:最大交易次数为1(leetcode121)

问题四:最大交易次数为2(leetcode123)

问题五:买卖股票最佳时间含手续费(leetcode714)

问题六:最佳股票买卖时机含冷冻期(leetcode309)


问题一:买卖股票的最佳时机IV(leetcode188)

问题描述:

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

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

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iv
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

大体思路:

二维数组dp0[i][j]表示在第i天,最大可交易次数为j时不持有股票的最大收益;

同理dp1[i][j]表示在第天,最大可交易次数为j时持有股票的最大收益。i从0到最大天数,j从0到k。

dp0[i][j] = max(dp0[i - 1][j], dp1[i - 1][j] + prices[i])

dp1[i][j] = max(dp1[i - 1][j], dp0[i - 1][j - 1] - prices[i])

第一个式子:当天没有股票有两种可能,要么前一天就没持有股票;要么为前一天持有股票,当天卖了。

第二个式子:当天持有股票有两种可能,要么前一天就持有股票,或者前一天没持有股票,当天买入了。

注:这里需要注意的是第二个式子中,第二种情况下的前一天的最大可交易次数为j-1而不为j,这是由于买入的次数要小于最大买入次数,假设为dp0[i - 1][j] 他可能在i-1天之前已经进行过j次交易了,在第i天坑定不能再买入了。

传递函数有了,下面描述其的初值。

我们从传递函数发现,当前值总是与其正上方的值和左上角的值有关,因此只要对dp0和dp1的第一列第一行初始化即可。

dp0的第一列和第一行均为0,首先第一列j=0,由于最大可交易次数为0,那坑定不能买了为0,第一行i= 0,由于在第1天,不持有股票只有一种可能就是没有买所以为0。

dp1的第一列,j= 0最大可交易次数为0,但是还要持有,这是不可能实现的,因此为非法状态统统设为负无穷;第一行(除第一个元素)i = 0,可交易次数大于0了,在第1天想持有股票,只能买第一天的,因此统统设为 -1 * prices[0]。

实现代码如下:

class Solution {
    public int maxProfit(int k, int[] prices) {
        if(k == 0 || prices.length <= 1){
            return 0;
        }
        if(k > prices.length / 2){
            return maxProfit(prices);
        }
        int[][] dp0 = new int[prices.length][k + 1];
        int[][] dp1 = new int[prices.length][k + 1];
        /* dp0[i][0] 和 dp0[0][j] 均为0*/
        for(int i = 0; i < prices.length; i++){
            dp1[i][0] = Integer.MIN_VALUE;
        }
        for(int j = 1; j < k + 1; j++){
            dp1[0][j] = -1 * prices[0];
        }
        for(int i = 1; i < prices.length; i++){
            for(int j = 1; j < k + 1; j++){
                dp0[i][j] = Math.max(dp0[i - 1][j], dp1[i - 1][j] + prices[i]);
                dp1[i][j] = Math.max(dp1[i - 1][j], dp0[i - 1][j - 1] - prices[i]);
            }
        }
        return dp0[prices.length - 1][k];
    }
    public int maxProfit(int[] prices) {
        if(prices.length <= 1){
            return 0;
        }
        int[] dp0 = new int[prices.length];
        int[] dp1 = new int[prices.length];
        dp1[0] = -1 * prices[0];
        for(int i = 1; i < dp0.length; i++){
            dp0[i] = Math.max(dp0[i - 1], dp1[i - 1] + prices[i]);
            dp1[i] = Math.max(dp1[i - 1], dp0[i - 1] - prices[i]);
        }
        return dp0[dp0.length - 1];
    }
}

由于k可能很大,当k > n / 2时问题就会转化为不用考虑最大交易次数的情况,可以认为最大交易次数为无穷。后面的代码下一节将会分析。

问题二:最大可交易次数不限(leetcode122)

该问题是问题一中k = 无穷的特例,此时 k = k - 1,因此转移函数变为

dp0[i] = max(dp0[i - 1], dp1[i - 1] + prices[i])

dp1[i] = max(dp1[i - 1], dp0[i - 1] - prices[i])

问题由二维dp转化为一维dp

初值为:d0[0] = 0,没买肯定是0啊, d1[0] = -prices[0],买了肯定是- prices[0]。

实现代码如下:

    public int maxProfit(int[] prices) {
        if(prices.length <= 1){
            return 0;
        }
        int[] dp0 = new int[prices.length];
        int[] dp1 = new int[prices.length];
        dp1[0] = -1 * prices[0];
        for(int i = 1; i < dp0.length; i++){
            dp0[i] = Math.max(dp0[i - 1], dp1[i - 1] + prices[i]);
            dp1[i] = Math.max(dp1[i - 1], dp0[i - 1] - prices[i]);
        }
        return dp0[dp0.length - 1];
    }

 问题三:最大交易次数为1(leetcode121)

dp0[i][j] = max(dp0[i - 1][j], dp1[i - 1][j] + prices[i])

dp1[i][j] = max(dp1[i - 1][j], dp0[i - 1][j - 1] - prices[i])

将上式中的j换成1,得到如下:

dp0[i][1] = max(dp0[i - 1][1], dp1[i - 1][1] + prices[i])

dp1[i][1] = max(dp1[i - 1][1], dp0[i - 1][0] - prices[i])

由于dp0[i - 1][0] = 0,所以最终化简后的转移方程为:

dp0[i]= max(dp0[i - 1], dp1[i - 1]+ prices[i])

dp1[i]= max(dp1[i - 1], 0 - prices[i])

初值:dp0[0] = 0, dp1[0] = -prices[0]

该结果也可以使用自然语言理解,当天不持有的有两种情况,或者前一天就不持有,或者前一天持有当天卖出去了;当天持有的两种情况为:前一天就持有,或者前一天不会持有,当天买了。由于只能买一次,前一天不持有说明之前也没进行过交易,因此前一天不持有的最大收益为0.

实现代码如下:

    public int maxProfit(int[] prices) {
        if(prices.length <= 1){
            return 0;
        }
        int[] dp0 = new int[prices.length];//  dp0[i] i天不持有股票的最大利润
        int[] dp1 = new int[prices.length]; // 持有股票的最大利润 
        dp0[0] = 0;
        dp1[0] = -1 * prices[0];
        for(int i = 1; i < prices.length; i++){
            dp0[i] = Math.max(dp0[i - 1], dp1[i - 1] + prices[i]);
            dp1[i] = Math.max(dp1[i - 1], 0 - prices[i]);
        }
        return dp0[dp0.length - 1];
    }

当然由于只交易一次,因此还可以很多其他办法解决,比如可以使用贪心的策略,找到当前点之前最小的元素,即在前面那个位置买,在当前点卖。由于本文主要将dp,这里就不多加赘述了。

问题四:最大交易次数为2(leetcode123)

这问题没啥说的,就令k=2咯,直接搞。

代码如下:

    public int maxProfit(int[] prices) {
        if(prices.length <= 1){
            return 0;
        }
        int K = 2;
        int[][] dp0 = new int[prices.length][K + 1];
        int[][] dp1 = new int[prices.length][K + 1]; 
        // dp0[0][k] 和 dp0[i][0] 均为0
        for(int i = 0; i < prices.length; i++){ // 第一列 最大允许交易次数为0 还要持有 定为非法
             dp1[i][0] = Integer.MIN_VALUE;
        }
        for(int k = 1; k <= K; k++){ //第一行
            dp1[0][k] = -1 * prices[0];
        }
        for(int i = 1; i < prices.length; i++){
            for(int k = 1; k <= K; k++){
                dp0[i][k] = Math.max(dp0[i - 1][k], dp1[i - 1][k] + prices[i]);
                dp1[i][k] = Math.max(dp1[i - 1][k], dp0[i - 1][k - 1] - prices[i]);
            }
        }
        return dp0[prices.length - 1][K];

    }

问题五:买卖股票最佳时间含手续费(leetcode714)

问题描述:

给定一个整数数组 prices,其中第 i 个元素代表了第 i 天的股票价格 ;非负整数 fee 代表了交易股票的手续费用。

你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。

返回获得利润的最大值。

注意:这里的一笔交易指买入持有并卖出股票的整个过程,每笔交易你只需要为支付一次手续费。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

大体思路:

直接在k=无穷上改就行,由于每笔交易都含手续费,因此在卖的时候加上手续费就行,转移方程如下:

dp0[i] = max(dp0[i - 1], dp1[i - 1] + prices[i] - fee)

dp1[i] = max(dp1[i - 1], dp0[i - 1] - prices[i])

初值 dp0[0] = 0, dp1[0] = - prices[0]

代码如下:

    public int maxProfit(int[] prices, int fee) {
        if(prices.length <= 1){
            return 0;
        }
        int[] dp0 = new int[prices.length];
        int[] dp1 = new int[prices.length];
        dp1[0] = -1 * prices[0];
        for(int i = 1; i < prices.length; i++){
            dp0[i] = Math.max(dp0[i - 1], dp1[i - 1] + prices[i] - fee);
            dp1[i] = Math.max(dp1[i - 1], dp0[i - 1] - prices[i]);
        }
        return dp0[dp0.length - 1];
    }

问题六:最佳股票买卖时机含冷冻期(leetcode309)

问题描述:

给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。​

设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):

你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
示例:

输入: [1,2,3,0,2]
输出: 3 
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

大体思路:

由于当天卖出股票后第二天不能直接买入,也就是说只有前天没持有,昨天没持有,今天才可以进行买入。传递函数如下:

dp0[i] = max(dp0[i - 1], dp1[i - 1] + prices[i])

dp1[i] = max(dp1[i - 1], dp0[i - 2] - prices[i])

由于前天没有持有,昨天也没有持有,今天买入之前的收益是等于前天的收益的。因此为dp0[i - 2] - prices[i]

由于用到i- 2,初值除了赋i= 0,还应对 i = 1赋值。

        dp0[0] = 0;

        dp1[0] = 0 - prices[0];

        dp0[1] = Math.max(dp0[0], dp1[0] + prices[1]);

        dp1[1] = Math.max(dp1[0], dp0[0] - prices[1]);// 由于-1时坑定没卖出,因此为dp0[0] - prices[1]。

实现代码如下:

    public int maxProfit(int[] prices) {
        // return dfs(prices, 0, 0, 0, 0);
        if(prices.length <= 1){
            return 0;
        }
        int[] dp0 = new int[prices.length]; //dp0[i] 为i天不持有股票的最大收益
        int[] dp1 = new int[prices.length]; //
        dp0[0] = 0;
        dp1[0] = 0 - prices[0];
        dp0[1] = Math.max(dp0[0], dp1[0] + prices[1]);
        dp1[1] = Math.max(dp1[0], dp0[0] - prices[1]);
        for(int i = 2; i < prices.length; i++){
            dp0[i] = Math.max(dp0[i - 1], dp1[i - 1] + prices[i]);
            dp1[i] = Math.max(dp1[i - 1], dp0[i - 2] - prices[i]);
        }
        return dp0[prices.length - 1];

    }

 

 

 

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