买卖股票的最佳时机IV
. - 力扣(LeetCode)
至多可以购买k次,相比买卖股票的最佳时机III至多购买2次,区别在于次数不确定。
每买卖一次,dp数组的第二维度加2,dp数组的第二维度为2k+1(0-2k)
dp数组 vector<vector<int>>dp(prices.size(),vector<int>(2k+1,0))
表示第i天买入和卖出k次的最大金额
使用for循环遍历天和次数
for(int i = 1; i < prices.size();i++){for(int j = 1; j < 2k+1; j+= 2){}}
代码随想录 (programmercarl.com)
达到dp[i][1]状态,有两个具体操作:
- 操作一:第i天买入股票了,那么dp[i][1] = dp[i - 1][0] - prices[i]
- 操作二:第i天没有操作,而是沿用前一天买入的状态,即:dp[i][1] = dp[i - 1][1]
选最大的,所以 dp[i][1] = max(dp[i - 1][0] - prices[i], dp[i - 1][1]);
同理dp[i][2]也有两个操作:
- 操作一:第i天卖出股票了,那么dp[i][2] = dp[i - 1][1] + prices[i]
- 操作二:第i天没有操作,沿用前一天卖出股票的状态,即:dp[i][2] = dp[i - 1][2]
所以dp[i][2] = max(dp[i - 1][1] + prices[i], dp[i - 1][2])
初始化情况,当不购买时,全初始化为0,购买的情况都初始化为-prices[0],也用循环实现。
for (int j = 1; j < 2 * k; j += 2) {dp[0][j] = -prices[0];
}
最终代码
class Solution {
public:int maxProfit(int k, vector<int>& prices) {// 如果没有价格数据,则无法进行交易,返回 0if (prices.size() == 0) return 0;// 创建一个二维向量 dp,用于动态规划// dp[i][j] 表示第 i 天结束时,进行了 j 次交易(或部分交易)的最大利润// j 为偶数时表示没有持有股票,j 为奇数时表示持有股票vector<vector<int>> dp(prices.size(), vector<int>(2 * k + 1, 0));// 初始化第 0 天的情况// 第 0 天持有股票的情况,利润为 -prices[0]for (int j = 1; j < 2 * k; j += 2) {dp[0][j] = -prices[0];}// 动态规划计算从第 1 天到第 n-1 天的最大利润for (int i = 1; i < prices.size(); i++) {for (int j = 0; j < 2 * k - 1; j += 2) {// 第 i 天持有股票的最大利润是前一天持有股票的利润和前一天没有股票今天买入的利润的最大值dp[i][j + 1] = max(dp[i - 1][j + 1], dp[i - 1][j] - prices[i]);// 第 i 天没有股票的最大利润是前一天没有股票的利润和前一天持有股票今天卖出的利润的最大值dp[i][j + 2] = max(dp[i - 1][j + 2], dp[i - 1][j + 1] + prices[i]);}}// 返回最后一天进行了 k 次交易的最大利润return dp[prices.size() - 1][2 * k];}
};
算法的时间复杂度为O((k)*n),空间复杂度为O((k)*n)。
最佳买卖股票时间含冷冻期
. - 力扣(LeetCode)
考虑含冷冻期的问题,需要前两天来保证今天购入并持有股票的状态。
首先,仍然是两个表示今天的股票状态,无持有或持有。分别用0和1表示。
dp[i][0]表示今天结束手头无股票持有所能得到的最大金额。
dp[i][1]表示今天结束手头持有股票所能得到的最大金额。
dp[i][0]为两种情况的较大值,昨日持有,但今日卖出;昨日也为持有,今日也未购买
dp[i][0] = max(dp[i-1][0],dp[i-1][1] + prices[i]);
dp[i][1]为两种情况的较大值,考虑到冷冻期的存在,前天未持有,今天购入;昨天持有,今天也持有。 dp[i][1] = max(dp[i-2][0] - prices[i],dp[i-1][1]);
由此,我们需要对第1、2天进行初始化,dp[0][0] = 0,dp[0][1] = -prices[0],dp[1][0] = max(dp[0][0],dp[0][1] + prices[1]),dp[1][1] = max(dp[0][0] - prices[1],dp[0][1]);
从前向后遍历,最后返回dp[nums.size()-1][0],最后一天的卖出获得的最大金额。
class Solution {
public:int maxProfit(vector<int>& prices) {// 创建一个二维向量 dp,用于动态规划// dp[i][0] 表示第 i 天结束时没有股票的最大利润,dp[i][1] 表示第 i 天结束时持有股票的最大利润vector<vector<int>> dp(prices.size(), vector<int>(2, 0));// 如果只有一天,无法进行交易,返回 0if (prices.size() == 1) {return 0;}// 初始化第 0 天的情况// 第 0 天没有股票,利润为 0dp[0][0] = 0;// 第 0 天买入股票,利润为 -prices[0]dp[0][1] = -prices[0];// 初始化第 1 天的情况// 第 1 天没有股票,利润为 max(第 0 天没有股票的利润, 第 0 天买入第 1 天卖出的利润)dp[1][0] = max(dp[0][0], dp[0][1] + prices[1]);// 第 1 天买入股票,利润为 max(第 0 天没有股票买入股票的利润, 第 0 天买入股票的利润)dp[1][1] = max(dp[0][0] - prices[1], dp[0][1]);// 动态规划计算从第 2 天到第 n-1 天的最大利润for (int i = 2; i < prices.size(); i++) {// 第 i 天没有股票的最大利润是前一天没有股票的利润和前一天有股票今天卖出的利润的最大值dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i]);// 第 i 天有股票的最大利润是前一天没有股票今天买入的利润和前一天有股票的利润的最大值dp[i][1] = max(dp[i-2][0] - prices[i], dp[i-1][1]); // 注意这里只能从前两天没有股票的状态转移过来}// 返回最后一天没有股票最大利润return dp[prices.size()-1][0];}
};
算法的时间复杂度为O(n),空间复杂度为O(n)。
买卖股票的最佳时机含手续费
. - 力扣(LeetCode)
题目相比之前的买卖股票的最佳时机II加入了手续费。
只改变了dp数组的推导和初始赋值。
完成一次买入卖出交易需要一次手续费,我们假定在买入时即完成手续费的交易,则dp[i][0]和dp[i][1]的推导如下
dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i]);
dp[i][1] = max(dp[i-1][0] - prices[i] - fee, dp[i-1][1]);
初始赋值dp[0][0] = 0;dp[0][1]=-prices[i]-fee;
其他相比买卖股票的最佳时机II没有变化。
class Solution {
public:int maxProfit(vector<int>& prices, int fee) {// dp[i][0] 表示第 i 天结束时没有股票的最大利润,dp[i][1] 表示第 i 天结束时持有股票的最大利润vector<std::vector<int>> dp(prices.size(), std::vector<int>(2, 0));// 初始化第 0 天的情况// 第 0 天没有股票,利润为 0dp[0][0] = 0;// 第 0 天买入股票,利润为 -prices[0] - fee(因为需要支付交易费用)dp[0][1] = -prices[0] - fee;// 动态规划计算从第 1 天到第 n-1 天的最大利润for(int i = 1; i < prices.size(); i++) {// 第 i 天没有股票的最大利润是前一天没有股票的利润和前一天有股票今天卖出的利润的最大值dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i]);// 第 i 天有股票的最大利润是前一天没有股票今天买入的利润和前一天有股票的利润的最大值// 买入时需要支付交易费用dp[i][1] = max(dp[i-1][0] - prices[i] - fee, dp[i-1][1]);}// 返回最后一天没有股票的最大利润return dp[prices.size()-1][0];}
};
算法的时间复杂度和空间复杂度为O(n)。