股票买卖问题全家桶(已团灭)

简介: 股票买卖问题全家桶(已团灭)

"股票买卖问题"总体分析

限制条件


先买入才能卖出

不能同时参加多笔交易,再次买入时,需要先卖出

k >= 1才能进行交易,否则没有交易次数


定义操作

买入(buy)

卖出(sell)

不操作(沿用上一次的数据)


定义状态

i:天数

k:交易次数,每次交易包含买入和卖出,这里我们只在买入的时候需要将k - 1(这里解释一下:因为题目规定如果手中有股票是不能继续买入的,所以买入和卖出是一组连续的操作,我们只需要在其中一处-1即可。)

0:不持有股票

1:持有股票

举例

举例
dp[i][k][0]//第i天还可以交易k次手中没有股票
dp[i][k][1]//第i天还可以交易k次手中有股票
最终的最大收益是dp[n - 1][k][0]而不是dp[n - 1][k][1],
因为最后一天卖出肯定比持有收益更高

状态转移方程

//今天没有持有股票,分为两种情况:
// 1. dp[i - 1][k][0],昨天没有持有,今天不操作。
// 2. dp[i - 1][k][1] + prices[i]昨天持有,今天卖出,今天手中就没有股票了。
dp[i][k][0] = Math.max( dp[i - 1][k][0],dp[i - 1][k][1] + prices[i])
//今天持有股票,分为两种情况:
// 1.dp [i - 1][k][1]昨天持有,今天不操作
// 2.dp[i - 1][k - 1][0] - prices [i]昨天没有持有,今天买入。
dp[i][k][1] = Math.max(dp[i - 1][k][1],dp[i - 1][k - 1][0] - prices[i])
//最大利润就是这俩种情况的最大值

下面这张图可以帮助理解状态转移方程的由来

题目

121. 买卖股票的最佳时机(easy)

题目:

给定一个数组 prices ,它的第i 个元素prices[i] 表示一支给定股票第 i 天的价格。

你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。

返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。

分析

//第i天不持有由第i-1天不持有然后不操作和第i-1天持有然后卖出两种情况的最大值转移过来
 dp[i][1][0] = Math.max(dp[i - 1][1][0], dp[i - 1][1][1] + prices[i])
//第i天持有由第i-1天持有然后不操作和第i-1天不持有然后买入两种情况的最大值转移过来
 dp[i][1][1] = Math.max( dp[i - 1][1][1],dp[i - 1][0][0] - prices[i])
//           = Math.max(dp[i-1][1][1],-prices[i])// k=0时没有交易次数,dp[i-1][0][0] = 0
k是固定值1,不影响结果,所以可以不用管,简化之后如下
dp[i][0] = Math.max(dp[i - 1][0],dp[i - 1][1] + prices[i])
dp[i][1] = Math.max ( dp[i - 1][1],-prices [i])

code

public class dp_买卖股票的最佳时机121 {
    public static void main(String[] args) {
        int[] num = {7, 1, 5, 3, 6, 4};
//        int[] num = {7, 6, 4, 3, 1};
        System.out.println(maxProfit(num));
    }
    /*//完整代码
    public static int maxProfit(int[] prices) {
        int length = prices.length;
        int[][] dp = new int[length][2];
        dp[0][0] = 0;//第0天不持有
        dp[0][1] = -prices[0];//第0天持有
        for (int i = 1; i < length; 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], -prices[i]);
        }
        return dp[length - 1][0];
    }*/
    /*//状态压缩,利用滚动数组降维
    public static int maxProfit(int[] prices) {
        int length = prices.length;
        int[] dp = new int[2];
        dp[0] = 0;//第0天不持有
        dp[0] = -prices[0];//第0天持有
        for (int i = 1; i < length; i++) {
            dp[0] = Math.max(dp[0], dp[1] + prices[i]);
            dp[1] = Math.max(dp[1], -prices[i]);
        }
        return dp[0];
    }*/
    //语义化
    public static int maxProfit(int[] prices) {
        int sell = 0;//出售
        int buy = -prices[0];//买入
        for (int i = 1; i < prices.length; i++) {
            sell = Math.max(sell, buy + prices[i]);
            buy = Math.max(buy, -prices[i]);
        }
        return sell;
    }
}

这道题还有一个我自己实现的版本,

思路是:前i天的最大收益 = max{前i-1天的最大收益,第i天的价格-前i-1天中的最小价格}

public class dp_买卖股票的最佳时机121_self {
    //        动态规划 前i天的最大收益 = max{前i-1天的最大收益,第i天的价格-前i-1天中的最小价格}
    public static void main(String[] args) {
        int[] num = {7, 1, 5, 3, 6, 4};
//        int[] num = {7, 6, 4, 3, 1};
        System.out.println(maxProfit(num));
    }
    public static int maxProfit(int[] prices) {
        int[] dp = new int[prices.length];
        int min = prices[0];
        for (int i = 1; i < prices.length; i++) {
            dp[i] = Math.max(dp[i - 1], prices[i] - min);
            min = Math.min(min, prices[i]);
        }
        return dp[dp.length - 1];
    }
}

122. 买卖股票的最佳时机 II(medium)

题目:

给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。

在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。

返回 你能获得的 最大 利润。

分析

//第i天不持有由第i-1天不持有然后不操作和第i-1天持有然后卖出两种情况的最大值转移过来
dp[i][k][0] = Math.max(dp[i - 1][k][0],dp[i - 1][k][1] + prices[i])
//第i天持有由第i-1天持有然后不操作和第i-1天不持有然后买入两种情况的最大值转移过来
dp[i][k][1] = Math.max(dp[i - 1][k][1],dp[i - 1][k - 1][0] - prices[i])
k同样不影响结果,简化之后如下
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])

code

public class dp_买卖股票的最佳时机122 {
    public static void main(String[] args) {
//        int[] num = {7, 1, 5, 3, 6, 4};
        int[] num = {1, 2, 3, 4, 5};
        System.out.println(maxProfit(num));
    }
    //    完整代码
    /*public static int maxProfit(int[] prices) {
        int length = prices.length;
        int[][] dp = new int[length][2];
        dp[0][0] = 0;//第0天不持有
        dp[0][1] = -prices[0];//第0天买入 花了prices[0]元
        for (int i = 1; i < length; 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[length - 1][0];
    }*/
    //  状态压缩,利用滚动数组去掉一维
    /*public static int maxProfit(int[] prices) {
        int length = prices.length;
        int[] dp = new int[2];
        dp[0] = 0;
        dp[1] = -prices[0];
        for (int i = 1; i < length; i++) {
            dp[0] = Math.max(dp[0], dp[1] + prices[i]);
            dp[1] = Math.max(dp[1], dp[0] - prices[i]);
        }
        return dp[0];
    }*/
    //语义化
    public static int maxProfit(int[] prices) {
        int sell = 0;
        int buy = -prices[0];
        for (int i = 1; i < prices.length; i++) {
            sell = Math.max(sell, buy + prices[i]);
            buy = Math.max(buy, sell - prices[i]);
        }
        return sell;
    }
}

123. 买卖股票的最佳时机 III(hard)

题目:

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

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

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

状态转移方程
dp[i][k][0] = Math.max( dp[i - 1][k][0],dp[i - 1][k][1] + prices[i])
dp[i][k][1] = Math.max(dp[i - 1][k][1],dp[i - 1][k - 1][0] - prices[i])
k对结果有影响不能舍取,只能对k进行循环
for ( let i = 0; i < n; i++) {
    for (let k = maxK; k >= 1; k--) {
        dp[i][k][0] = Math.max( dp[i - 1][k][0],dp[i - 1][k][1] + prices[i] );
        dp[i][k][1] = Math.max(dp[i - 1][k][1],dp[i - 1][k - 1][0] - prices[i] );
    }
}
// k=2,由于k很小,这里直接写出循环的结果来进一步优化
dp[i][2][0] = Math.max(dp[i - 1][2][0],dp[i - 1][2][1] + prices[i])
dp[i][2][1] = Math.max(dp[i - 1][2][1], dp[i - 1][1][0] - prices[i])
dp[i][1][0] = Math.max(dp[i - 1][1][0],dp[i - 1][1][1] + prices[i])
dp[i][1][1] = Math.max( dp[i - 1][1][1],dp[i - 1][0][0] - prices[i])
            = Math.max(dp[i - 1][1][1],-prices[i])// k=0时没有交易次数,dp[i - 1][0][0] = 0
//去掉i这一维度
dp[2][0] = Math.max( dp[2][0], dp[2][1] + prices[i])
dp[2][1] = Math.max(dp [2] [1], dp[1][0] - prices[i])
dp[1][0] = Math.max( dp[1][0], dp[1][1] + prices [i])
dp[1][1] = Math.max( dp[1][1], dp[0][0] - prices [i] )
         = Math.max(dp[1][1],-prices[i])// k=0时没有交易次数,dp[i - 1][0][0] = 0

code

public class dp_买卖股票的最佳时机123 {
    public static void main(String[] args) {
        int[] num = {3, 3, 5, 0, 0, 3, 1, 4};
//        int[] num = {1, 2, 3, 4, 5};
        System.out.println(maxProfit(num));
    }
    //完整代码 + 降维 + 语义化
    public static int maxProfit(int[] prices) {
        int length = prices.length;
        int sell1 = 0, sell2 = 0;
        int buy1 = -prices[0], buy2 = -prices[0];
        for (int i = 1; i < length; i++) {
            sell2 = Math.max(sell2, buy2 + prices[i]);
            buy2 = Math.max(buy2, sell1 - prices[i]);
            sell1 = Math.max(sell1, buy1 + prices[i]);
            buy1 = Math.max(buy1, -prices[i]);
        }
        return sell2;
    }
}

188. 买卖股票的最佳时机 IV(hard)

题目:

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

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

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

总体情况和122题类似,

122题可以交易无数次,188交易k次,所以直接在加一层k循环就可以

122最后的递推方程:

sell = Math.max( sell,buy + prices [i] );

buy = Math.max ( buy, -prices[i] );

code

public class dp_买卖股票的最佳时机188 {
    public static void main(String[] args) {
        int[] num = {2, 4, 1};
//        int[] num = {3,2,6,5,0,3};
        System.out.println(maxProfit(2, num));
    }
    //完整代码 + 降维 + 语义化
    public static int maxProfit(int k, int[] prices) {
        int n = prices.length;
        int[][] dp = new int[k + 1][2];//和123题—样求出所有k的状态
        //初始化k次交易买入卖出的利润
        for (int i = 0; i <= k; i++) {
            dp[i][0] = -prices[0];//buy  表示有股票
            dp[i][1] = 0;//sell 表示没有股票
        }
        for (int price : prices) {
            for (int j = 1; j <= k; j++) {
                //122题可以交易无数次,188交易k次,所以直接在加一层k循环就可以
                //122最后的递推方程:
                //sell = Math.max( sell,buy + prices [i] );
                //buy = Math.max ( buy, -prices[i] );
                dp[j][1] = Math.max(dp[j][1], dp[j][0] + price);
                dp[j][0] = Math.max(dp[j][0], dp[j - 1][1] - price);
            }
        }
        return dp[k][1];//返回第k次清空手中的股票之后的最大利润
    }
}

309. 买卖股票的最佳时机含冷冻期(medium)

题目:

给定一个整数数组prices,其中第 prices[i] 表示第 i 天的股票价格 。

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

卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。

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

分析

状态转移方程
dp[i][k][0] = Math.max( dp[i - 1][k][0],dp[i - 1][k][1] + prices[i])
//冷却时间1天,所以要从i - 2天转移状态
//买入,卖出—---冷冻期—---买入,卖出
dp[i][k][1] = Math.max(dp[i - 1][k][1],dp[i - 2][k - 1][0] - prices[i])
题目不限制k的大小,可以舍去
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 - 2][0] - prices[i])
//降维i
dp[0] = Math.max(dp[0], dp[1] + prices [i])
dp[1] = Math.max (dp[1], profit_freeze - prices [i])

code

public class dp_买卖股票的最佳时机含冷冻期309 {
    public static void main(String[] args) {
//        int[] num = {1, 2, 3, 0, 2};//3
        int[] num = {1};//0
        System.out.println(maxProfit(num));
    }
    //完整代码 + 降维 + 语义化
    public static int maxProfit(int[] prices) {
        int n = prices.length;
        int buy = -prices[0];//手中有股票
        int sell = 0;//没有股票
        int profit_freeze = 0;
        for (int i = 1; i < n; i++) {
            int temp = sell;
            sell = Math.max(sell, buy + prices[i]);
            buy = Math.max(buy, profit_freeze - prices[i]);
            profit_freeze = temp;
        }
        return sell;
    }
}

714. 买卖股票的最佳时机含手续费(medium)

题目:

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

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

返回获得利润的最大值。

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

分析

状态转移方程
//每次交易要支付手续费我们定义在卖出的时候扣手续费
dp[i][0] = max(dp[i - 1][0],dp[i - 1][1] + prices [i] - fee)
dp[i] [1] = max(dp[i - 1][1], dp[i - 1][0] - prices[i])

code

public class dp_买卖股票的最佳时机含手续费714 {
    public static void main(String[] args) {
        int fee = 2;
//        int fee = 3;
        int[] num = {1, 3, 2, 8, 4, 9};//8
//        int[] num = {1,3,7,5,10,3};//6
        System.out.println(maxProfit(num, fee));
    }
    //完整代码 + 降维 + 语义化
    public static int maxProfit(int[] prices, int fee) {
        int sell = 0;
        int buy = -prices[0];
        for (int price : prices) {
            sell = Math.max(sell, buy + price - fee);
            buy = Math.max(buy, sell - price);
        }
        return sell;
    }
}

总结

"股票买卖问题"是动态规划的一种,核心思想还是那句话:“当前的结果都是由之前的过程推导出来的” ,需要注意的是,股票问题属于一类问题,我们应对其进行一个总体的思考,根据限制条件和相应操作来定义状态,本题需要定义三个状态,但是具体到每道题的话,我们可以将不需要的状态去掉,并且可以通过滚动数组降维,从而进行状态压缩,最后通过适当的语义化来增强可读性。


参考leetcode中的视频讲解:一种解法团灭买卖股票问题


文章粗浅,希望对大家有帮助!


相关文章
|
人工智能
adobe2023全家桶阿里云
Adobe系列的软件都挺好的,需要做图、设计的工作者,基本都会用到Adobe系列的软件,一张张优秀图片的制作离不开这些软件的综合运用。
713 0
|
存储 人工智能 编解码
adobe全家桶2023百度网盘分享
最新Adobe2023全集桶 正式版,更强大的内容,更完善的功能,更全面的软件,给你带来全新不一样的体验。今天我为各位小伙伴们找到了Adobe全家桶的下载资源,不用再瞎找了,这篇文章就够了。含PS、PR、DW、LR、AE、ID、AI、XD、AU、RU等等。(收集不易,感谢支持) !
3747 2
|
安全 测试技术
Fiddler入门教程全家桶,建议收藏
Fiddler入门教程全家桶,建议收藏
146 1
|
7月前
|
存储 JavaScript 前端开发
vue全家桶详解
vue全家桶详解
118 1
|
7月前
|
JavaScript 前端开发 API
VUE3全家桶精讲
VUE3全家桶精讲
206 0
|
7月前
|
算法
【MATLAB】史上最全的5种数据插值算法全家桶
【MATLAB】史上最全的5种数据插值算法全家桶
118 0
|
JavaScript 前端开发
vue全家桶有哪些?
vue全家桶有哪些?
|
存储 JavaScript 前端开发
vue全家桶
Vue脚手架、Vuex、Vue-router和Axios是Vue.js的核心工具,它们分别提供了一套完整的开发环境、状态管理、路由管理和网络请求。在本篇博客中,我们介绍了它们的使用方式、实际应用场景以及底层实现原理。希望本篇博客能够对大家有所启发,为大家的Vue开发之路提供帮助。
97 0
|
Java Spring
Spring全家桶
Spring全家桶
53 0
|
SQL 监控 NoSQL
常用服务端口号-你知道的和不知道的都在这(全家桶)
常用服务端口号-你知道的和不知道的都在这(全家桶)
143 0