LeetCode:买卖股票系列( 121. 简单,122. 简单,123. 困难,188: 困难, 309:中等,714:中等)

121: I题目描述:

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
如果你最多只允许完成一笔交易(即买入和卖出一支股票一次),设计一个算法来计算你所能获取的最大利润。
注意:你不能在买入股票前卖出股票。
示例 1:
输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。

这系列通用题解见最后

思想: 动态规划

dp[i]表示前i天的最大利润。

  • 步骤:
  1. 确定dp数组以及下标的含义;
  2. 确定初始条件,如 dp(0);
    前0天,前1天最大利润dp[0],dp[1]均为0。
  3. 根据 dp(i)和 dp(i-1)的关系得出状态转移方程;
dp[i] = max(dp[i - 1], prices[i] - minprice);
  1. 求最值

I题解

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        if (n == 0) return 0; // 边界条件
        int minprice = prices[0];
        vector<int> dp (n, 0);
        for (int i = 1; i < n; i++){
            minprice = min(minprice, prices[i]);
            dp[i] = max(dp[i - 1], prices[i] - minprice);
        }
        return dp[n - 1];
    }
};

122: II题目描述:

你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔

思想:贪心算法

每次选择对于当前最好.如果当天股价高于前一天,则前一天买入,当天卖出.

II题解

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int maxProfit = 0;
        for(int i = 1; i < prices.size(); i++)
        {
            maxProfit += max(prices[i] - prices[i-1], 0);
        }
        return maxProfit;
    }
};

动态规划思想参见暴力搜索、贪心算法、动态规划方法三.

动态规划步骤

第1步:定义状态

状态dp[i][j]定义如下:

  • 第一维 i 表示索引为 i 的那一天(具有前缀性质,即考虑了之前天数的收益)能获得的最大利润;
  • 第二维 j 表示索引为 i 的那一天是持有股票,还是持有现金。这里 0 表示持有现金(cash),1 表示持有股票(stock)。
第2步:定义状态转移方程

第i天仅有两种状态:持有或者不持有.

  • 持有状态
  1. 今天买入:则昨天是不持有的状态,当天利益为dp[i-1][0]-prices[i];
  2. 前一天也持有:今天没有卖出,当天利益为dp[i-1][1];取其大者作为当天持有状态下的利益。
  • 非持有状态
  1. 今天卖出:则昨天是持有的状态,当天利益为dp[i-1][1]+prices[i];
  2. 前一天也不持有:则今天也没有买入,当天利益就是前一天利益,即dp[i-1][0];
    取其大者作为当天非持有状态下的利益。
第3步:确定起始状态
  • 如果什么都不做,dp[0][0] = 0;
  • 如果买入股票,当前收益是负数,即 dp[0][1] = -prices[0];
第4步:确定终止
  • 终止的时候,上面也分析了,输出 dp[len - 1][0],因为一定有 dp[len - 1][0] > dp[len - 1][1]。

II题解

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        // 动态规划解法
        // 0.初始判断
        if(prices.empty()) return 0; 
        int n = prices.size();
        if( n == 1) return 0; 
        int dp[prices.size()][2];
        // 1.初始状态
        dp[0][0] = 0;
        dp[0][1] = -prices[0];
        // 2.状态转移
        for(int i = 1; i<prices.size();i++){
            // 3.状态转移方程
            dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i]);
            dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i]);
        }
        return dp[prices.size()-1][0];
    }
};

123: III 题目描述:

你最多可以完成两笔交易。
通用题解

思想:动态规划.

初值

初始状态分为两种,第一种是任何一天,交易次数至多0笔;第二种是第0天有至多1到k_max笔交易数.

dp[i][0][0] = 0,dp[i][0][1] = -infinity;
// 任何一天,至多0笔交易,不持有时最大利润为0,持有则不存在这种情况.
dp[0][k][0] = 0,dp[0][k][1] = -prices[0];
//第0天有至多1到k_max笔交易数.不持有时最大利润为0,持有意味着需要买入,则最大利润为-prices[0].

代码实现:

for(int i = 0; i < len; i ++ ) dp[i][0][0] = 0, dp[i][0][1] = INT_MIN;
for(int k = 1; k <= k_max; k ++ ) dp[0][k][0] = 0, dp[0][k][1] = -prices[0];

转移方程

dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i])
//解释:今天我没有持有股票,有两种可能:
//要么是我昨天就没有持有,我今天还是没有持有;
//要么是我昨天持有股票,但是今天我 sell 了,所以我今天没有持有股票了。
dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i])
//解释:今天我持有着股票,有两种可能:
//要么我昨天就持有着股票,但是今天不sell,所以我今天还持有着股票;
//要么我昨天本没有持有,但今天我选择 buy,所以今天我就持有股票了。

最值 dp[len - 1][k_max][0]

即最后一天,最多允许k_max次交易,此时不持有股票,最多获得多少利润。

III题解

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int len = prices.size();
        if (len == 0)
        {
            return 0;
        }
        int k_max = 2;//k_max最大交易次数,k为至今最多交易次数
       // 多维数组初始化
        //vector<vector<vector<int>>> dp(len,vector<vector<int>>(k_max+1,vector<int>(2,0)));
        int dp[len][k_max+1][2];
        //初始状态分为两种,第一种是任何一天,交易次数至多0笔;第二种是第0天有至多1到k_max笔交易数.
        for(int i = 0; i < len; i ++ ) dp[i][0][0] = 0, dp[i][0][1] = INT_MIN;
        for(int k = 1; k <= k_max; k ++ ) dp[0][k][0] = 0, dp[0][k][1] = -prices[0];
        for(int i = 1; i < len; i ++ ) {
            for(int k = 1; k <= k_max; k++ )
            {
                dp[i][k][0] = max(dp[i - 1][k][0], dp[i - 1][k][1] + prices[i]);
                dp[i][k][1] = max(dp[i - 1][k][1], dp[i - 1][k - 1][0] - prices[i]);
            }
        }
        return dp[len - 1][k_max][0];
    }
};

188: IV 题目描述:

你最多可以完成k笔交易。

解答过程参见III中分析

IV题解

class Solution {
public:
    int maxProfit(int k, vector<int>& prices) {
        int len = prices.size();
        if (len == 0)
        {
            return 0;
        }
        int k_max = k;//k_max最大交易次数,k为至今最多交易次数
       // 多维数组初始化
        //vector<vector<vector<int>>> dp(len,vector<vector<int>>(k_max+1,vector<int>(2,0)));
        int dp[len][k_max+1][2];
        //初始状态分为两种,第一种是任何一天,交易次数至多0笔;第二种是第0天有至多1到k_max笔交易数.
        for(int i = 0; i < len; i ++ ) dp[i][0][0] = 0, dp[i][0][1] = INT_MIN;
        for(int k = 1; k <= k_max; k ++ ) dp[0][k][0] = 0, dp[0][k][1] = -prices[0];
        for(int i = 1; i < len; i ++ ) {
            for(int k = 1; k <= k_max; k++ )
            {
                dp[i][k][0] = max(dp[i - 1][k][0], dp[i - 1][k][1] + prices[i]);
                dp[i][k][1] = max(dp[i - 1][k][1], dp[i - 1][k - 1][0] - prices[i]);
            }
        }
        return dp[len - 1][k_max][0];
    }
};

309. 最佳买卖股票时机含冷冻期

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

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

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

动态规划步骤

第1步:定义状态

状态dp[i][j]定义如下:

  • 第一维 i 表示索引为 i 的那一天(具有前缀性质,即考虑了之前天数的收益)能获得的最大利润;
  • 第二维 j 表示索引为 i 的那一天是持有股票,还是持有现金。这里 0 表示持有现金(cash),1 表示持有股票(stock)。
第2步:定义状态转移方程

第i天仅有两种状态:持有或者不持有.

  • 持有状态dp[i][1]
  1. 今天买入:则前天(昨天的昨天)是不持有的状态,当天利益为dp[i-2][0]-prices[i];
  2. 前一天也持有:今天没有卖出,当天利益为dp[i-1][1];取其大者作为当天持有状态下的利益。
  • 非持有状态dp[i][0]
  1. 今天卖出:则昨天是持有的状态,当天利益为dp[i-1][1]+prices[i];
  2. 前一天也不持有:则今天也没有买入,当天利益就是前一天利益,即dp[i-1][0];
    取其大者作为当天非持有状态下的利益。
第3步:确定起始状态
  • 如果什么都不做,dp[0][0] = 0;
  • 如果买入股票,当前收益是负数,即 dp[0][1] = -prices[0];
第4步:确定终止
  • 终止的时候,上面也分析了,输出 dp[len - 1][0],因为一定有 dp[len - 1][0] > dp[len - 1][1]。

包含冷冻期题解

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        // 动态规划解法
        // 0.初始判断
        if(prices.empty()) return 0; 
        int n = prices.size();
        if( n == 1) return 0; 

        int dp[prices.size()][2];
        // 1.初始状态
        dp[0][0] = 0;
        dp[0][1] = -prices[0];
        dp[1][0] = max(dp[0][0],dp[0][1] + prices[1]);//由于i从2开始,初始值需要重新定义
        dp[1][1] = max(dp[0][1],dp[0][0]-prices[1]);
        // 2.状态转移
        for(int i = 2; i < prices.size();i++){
            // 3.状态转移方程
            dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i]);
            dp[i][1] = max(dp[i-1][1], dp[i-2][0] - prices[i]);
        }
        return dp[prices.size()-1][0];
    }
};

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

思路见II可以不限次数买入卖出或者冷冻期部分。

包含手续费题解

class Solution {
public:
    int maxProfit(vector<int>& prices, int fee) {
                // 动态规划解法
        // 0.初始判断
        if(prices.empty()) return 0; 
        int n = prices.size();
        if( n == 1) return 0; 

        int dp[prices.size()][2];
        // 1.初始状态
        dp[0][0] = 0;
        dp[0][1] = -prices[0]; //如果买入时支付手续费,则初始买入时也应该正确赋值dp[0][1] = -prices[0] - fee;

        // 2.状态转移
        for(int i = 1; i < prices.size();i++){
            // 3.状态转移方程
            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]);//这里的一笔交易指买入持有并卖出股票的整个过程,每笔交易你只需要为支付一次手续费。
        }
        return dp[prices.size()-1][0];

    }
};

121. 买卖股票的最佳时机
股票问题(Python3、C++)
买卖股票的最佳时机 II
买卖股票的最佳时机II-贪心算法+DP
暴力搜索、贪心算法、动态规划
动态规划-最简洁的介绍状态转移
五种实现+详细图解 123.买卖股票的最佳时机 III
一个方法团灭 LeetCode 股票买卖问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值