所有的LeetCode题解索引,可以看这篇文章——【算法和数据结构】LeetCode题解。
思路分析:
程序如下:
// 121、股票I-动态规划
class Solution {
public:
int maxProfit(vector<int>& prices) {
if (prices.size() == 0) return 0;
vector<vector<int>> dp(prices.size(), vector<int>(2)); // 二维数组,行代表天数,列代表是否持有股票
dp[0][0] -= prices[0];
dp[0][1] = 0;
for (int i = 1; i < prices.size(); i++) {
dp[i][0] = max(dp[i - 1][0], -prices[i]); // 第i天持有股票
dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0]); // 第i天不持有股票
}
return dp[prices.size() - 1][1];
}
};
复杂度分析:
在上面程序的基础之上,我们发现对于 d p [ i ] [ j ] dp[i][j] dp[i][j]来说,只利用了 d p [ i − 1 ] [ j ] dp[i-1][j] dp[i−1][j],因此空间不必开辟那么大。利用滚动数组,我们将空间复杂度降低到 O ( 1 ) O(1) O(1)。
程序如下:
// 121、股票I-动态规划-滚动数组
class Solution2 {
public:
int maxProfit(vector<int>& prices) {
if (prices.size() == 0) return 0;
vector<vector<int>> dp(2, vector<int>(2)); // 二维数组,行代表天数,列代表是否持有股票
dp[0][0] -= prices[0];
dp[0][1] = 0;
for (int i = 1; i < prices.size(); i++) {
dp[i % 2][0] = max(dp[(i - 1) % 2][0], -prices[i]); // 第i天持有股票
dp[i % 2][1] = max(dp[(i - 1) % 2][1], prices[i] + dp[(i - 1) % 2][0]); // 第i天不持有股票
}
return dp[(prices.size() - 1) % 2][1];
}
};
复杂度分析:
思路分析:本题与121题不同之处在于可以多次买卖,但是买入一只股票之前不能持有其他股票。因为这一点不一样,所以当第 i i i天买入股票的时候,所持有的现金可能有之前买卖过的利润。那么第 i i i天持有股票即 d p [ i ] [ 0 ] dp[i][0] dp[i][0],如果是第 i i i天买入股票,所得现金就是昨天不持有股票的所得现金减去今天的股票价格 即: d p [ i − 1 ] [ 1 ] − p r i c e s [ i ] dp[i - 1][1] - prices[i] dp[i−1][1]−prices[i]。其余部分的分析和121题完全一样。
程序如下:
// 122、股票II-动态规划
class Solution3 {
public:
int maxProfit(vector<int>& prices) {
if (prices.size() == 0) return 0;
vector<vector<int>> dp(prices.size(), vector<int>(2)); // 二维数组,行代表天数,列代表是否持有股票
dp[0][0] -= prices[0];
dp[0][1] = 0;
for (int i = 1; i < prices.size(); i++) {
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]); // 第i天持有股票
dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + prices[i]); // 第i天不持有股票
}
return dp[prices.size() - 1][1];
}
};
复杂度分析:
同理,本题也可以用滚动数组降低空间复杂度。程序如下:
// 122、股票II-动态规划-滚动数组
class Solution4 {
public:
int maxProfit(vector<int>& prices) {
if (prices.size() == 0) return 0;
vector<vector<int>> dp(2, vector<int>(2)); // 二维数组,行代表天数,列代表是否持有股票
dp[0][0] -= prices[0];
dp[0][1] = 0;
for (int i = 1; i < prices.size(); i++) {
dp[i % 2][0] = max(dp[(i - 1) % 2][0], dp[(i - 1) % 2][1] - prices[i]); // 第i天持有股票
dp[i % 2][1] = max(dp[(i - 1) % 2][1], dp[(i - 1) % 2][0] + prices[i]); // 第i天不持有股票
}
return dp[(prices.size() - 1) % 2][1];
}
};
复杂度分析:
思路分析:本题又是另外一个变体,最多可有完成两笔交易,同时买入一只股票之前不能持有其他股票。一天一共只有五个状态:1、没有操作;2、第一次持有股票;3、第一次不持有股票;4、第二次持有股票;5、第二次不持有股票。套用121题的思路,我们创建一个二维数组,行代表天数,列代表状态。
dp数组的含义。其中, d p [ i ] [ 0 ] dp[i][0] dp[i][0]代表第 i i i天没有操作; d p [ i ] [ 1 ] dp[i][1] dp[i][1]代表第 i i i天第一次持有股票; d p [ i ] [ 2 ] dp[i][2] dp[i][2]代表第 i i i天第一次不持有股票; d p [ i ] [ 3 ] dp[i][3] dp[i][3]代表第 i i i天第二次持有股票; d p [ i ] [ 4 ] dp[i][4] dp[i][4]代表第 i i i天第二次不持有股票。
递推公式。
除了 d p [ i ] [ 0 ] dp[i][0] dp[i][0]以外, d p [ i ] [ j ] dp[i][j] dp[i][j]都可以由两种情况得出。 d p [ i ] [ 0 ] dp[i][0] dp[i][0]代表没有操作,令 d p [ i ] [ 0 ] = d p [ i − 1 ] [ 0 ] dp[i][0] = dp[i - 1][0] dp[i][0]=dp[i−1][0]。 d p [ i ] [ 1 ] dp[i][1] dp[i][1]这种情况要么是在第 i i i天买了股票,要么是在之前买了股票:
二者取最大值 d p [ i ] [ 1 ] = m a x ( d p [ i − 1 ] [ 1 ] , d p [ i − 1 ] [ 0 ] − p r i c e s [ i ] ) dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i]) dp[i][1]=max(dp[i−1][1],dp[i−1][0]−prices[i])。这种情况要么是延续之前的状态,要么是在第 i i i天卖出:
二者取最大值 d p [ i ] [ 2 ] = m a x ( d p [ i − 1 ] [ 2 ] , d p [ i − 1 ] [ 1 ] − p r i c e s [ i ] ) dp[i][2] = max(dp[i-1][2], dp[i-1][1] - prices[i]) dp[i][2]=max(dp[i−1][2],dp[i−1][1]−prices[i])。同理可得剩下两个状态的递推公式:
dp[0][0] = 0; // 第一天没有操作
dp[0][1] -= prices[0]; // 第一天第一次持有股票 = 第一天买入
dp[0][2] = 0; // 第一天第一次不持有股票 = 第一天买入,又卖出
dp[0][3] -= prices[0]; // 第一天第二次持有股票 = 第一天买入,卖出,再买入
dp[0][4] = 0; // 第一天第二次不持有股票 = 第一天买入卖出两次
程序如下:
// 123、股票III-动态规划
class Solution {
public:
int maxProfit(vector<int>& prices) {
if (prices.size() == 0) return 0;
vector<vector<int>> dp(prices.size(), vector<int>(5)); // 二维数组,行代表天数,列代表不同状态
dp[0][0] = 0; // 第一天没有操作
dp[0][1] -= prices[0]; // 第一天第一次持有股票 = 第一天买入
dp[0][2] = 0; // 第一天第一次不持有股票 = 第一天买入,又卖出
dp[0][3] -= prices[0]; // 第一天第二次持有股票 = 第一天买入,卖出,再买入
dp[0][4] = 0; // 第一天第二次不持有股票 = 第一天买入卖出两次
for (int i = 1; i < prices.size(); i++) {
dp[i][0] = dp[i-1][0]; // 第i天没有操作
dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - prices[i]); // 第i天第一次持有股票
dp[i][2] = max(dp[i - 1][2], dp[i - 1][1] + prices[i]); // 第i天第一次不持有股票
dp[i][3] = max(dp[i - 1][3], dp[i - 1][2] - prices[i]); // 第i天第二次持有股票
dp[i][4] = max(dp[i - 1][4], dp[i - 1][3] + prices[i]); // 第i天第二次不持有股票
}
return dp[prices.size() - 1][4];
}
};
复杂度分析:
优化初始化的代码以及动态数组。程序当中,虽然dp[2]利用的是当天的dp[1],但结果也是对的。dp[1] = max(dp[1], dp[0] - prices[i]); 如果dp[1]取dp[1],即保持买入股票的状态,那么 dp[2] = max(dp[2], dp[1] + prices[i]);中dp[1] + prices[i] 就是今天卖出。如果dp[1]取dp[0] - prices[i],今天买入股票,那么dp[2] = max(dp[2], dp[1] + prices[i]);中的dp[1] + prices[i]相当于是今天再卖出股票,一买一卖收益为0,对所得现金没有影响。相当于今天买入股票又卖出股票,等于没有操作,保持昨天卖出股票的状态了。这种写法虽然简单,但是理解起来很复杂,使用上一个版本的代码足以。
// 123、股票III-动态规划-滚动数组
class Solution {
public:
int maxProfit(vector<int>& prices) {
if (prices.size() == 0) return 0;
vector<int> dp(5, 0); // 行代表天数,列代表不同状态
dp[1] -= prices[0]; // 第一天第一次持有股票 = 第一天买入
dp[3] -= prices[0]; // 第一天第二次持有股票 = 第一天买入,卖出,再买入
for (int i = 1; i < prices.size(); i++) {
dp[1] = max(dp[1], dp[0] - prices[i]); // 第i天第一次持有股票
dp[2] = max(dp[2], dp[1] + prices[i]); // 第i天第一次不持有股票
dp[3] = max(dp[3], dp[2] - prices[i]); // 第i天第二次持有股票
dp[4] = max(dp[4], dp[3] + prices[i]); // 第i天第二次不持有股票
}
return dp[4];
}
};
复杂度分析:
思路分析:本题是123、买卖股票的最佳时机 III的变体,买卖次数从两次变成了 k k k次。有了123题作为基础,我们很容易想到,买卖 k k k次其实就有了 2 k + 1 2k+1 2k+1中情况。我们创建一个行为 n n n,列为 2 k + 1 2k+1 2k+1的二维数组。我们根据显而易见的规律写出如下代码:
// 188、股票IV-动态规划
class Solution7 {
public:
int maxProfit(int k, vector<int>& prices) {
vector<vector<int>> dp(prices.size(), vector<int>(2 * k + 1, 0)); // 二维数组,行代表天数,列代表不同状态
for (int i = 1; i < 2 * k; i += 2) {
dp[0][i] -= prices[0]; // 第一天第i次持有股票
}
for (int i = 1; i < prices.size(); i++) {
for (int j = 0; j < 2 * k - 1; j +=2) {
dp[i][j + 1] = max(dp[i - 1][j + 1], dp[i - 1][j] - prices[i]); // 第i天第j次持有股票
dp[i][j + 2] = max(dp[i - 1][j + 2], dp[i - 1][j + 1] + prices[i]); // 第i天第j次不持有股票
}
}
return dp[prices.size() - 1][2 * k];
}
};
复杂度分析:
思路分析:本题是122题的变体,允许多次买卖,每次卖出之后存在冷冻期。因为存在冷冻期,我们将不持有股票情况分成两类:延续之前不持有股票的状态和今天卖出股票。因此这道题的情况有四种:1、持有股票;2、延续之前不持有股票的状态;3、今天卖出股票;4、冷冻期状态。
因为要获取最大利润,故我们在二者之间取最大值: d p [ i ] [ 0 ] = m a x ( d p [ i − 1 ] [ 0 ] , m a x ( d p [ i − 1 ] [ 1 ] , d p [ i − 1 ] [ 3 ] ) − p r i c e s [ i ] ) dp[i][0] = max(dp[i - 1][0], max(dp[i-1][1], dp[i-1][3])-prices[i]) dp[i][0]=max(dp[i−1][0],max(dp[i−1][1],dp[i−1][3])−prices[i])。
情况二, d p [ i ] [ 1 ] dp[i][1] dp[i][1]可以由状态二和冷冻期得出: d p [ i ] [ 1 ] = m a x ( d p [ i − 1 ] [ 1 ] , d p [ i − 1 ] [ 3 ] ) dp[i][1] = max(dp[i-1][1], dp[i-1][3]) dp[i][1]=max(dp[i−1][1],dp[i−1][3])
情况三,要想达到今天卖出股票的状态,那么昨天必然持有股票。 d p [ i ] [ 2 ] dp[i][2] dp[i][2]只能由 d p [ i − 1 ] [ 0 ] dp[i-1][0] dp[i−1][0]得出: d p [ i ] [ 2 ] = d p [ i − 1 ] [ 0 ] + p r i c e s [ i ] dp[i][2] = dp[i-1][0] + prices[i] dp[i][2]=dp[i−1][0]+prices[i]。
情况四,要想达到冷冻期,那么昨天必然卖出了股票。因此 d p [ i ] [ 3 ] dp[i][3] dp[i][3]只能由 d p [ i − 1 ] [ 2 ] dp[i-1][2] dp[i−1][2]得出: d p [ i ] [ 3 ] = d p [ i − 1 ] [ 2 ] dp[i][3] = dp[i-1][2] dp[i][3]=dp[i−1][2]。
dp[i][0] = max(dp[i - 1][0], max(dp[i-1][1], dp[i-1][3])-prices[i]); // 状态1
dp[i][1] = max(dp[i-1][1], dp[i-1][3]); // 状态2
dp[i][2] = dp[i-1][0] + prices[i]; // 状态3
dp[i][3] = dp[i-1][2]; // 状态4
return max(dp[prices.size() - 1][1], max(dp[prices.size() - 1][2], dp[prices.size() - 1][3]));
程序如下:
class Solution8 {
public:
int maxProfit(vector<int>& prices) {
vector<vector<int>> dp(prices.size(), vector<int>(4, 0)); // 二维数组,行代表天数,列代表不同状态
dp[0][0] = -prices[0];
for (int i = 1; i < prices.size(); i++) {
dp[i][0] = max(dp[i - 1][0], max(dp[i - 1][1], dp[i - 1][3]) - prices[i]); // 状态1
dp[i][1] = max(dp[i - 1][1], dp[i - 1][3]); // 状态2
dp[i][2] = dp[i - 1][0] + prices[i]; // 状态3
dp[i][3] = dp[i - 1][2]; // 状态4
}
return max(dp[prices.size() - 1][1], max(dp[prices.size() - 1][2], dp[prices.size() - 1][3]));
}
};
复杂度分析:
思路分析:本题是122题的变体,在122题的基础上加上了手续费。一次完整的交易(买入一次+卖出一次)需要支付一次手续费。我们可以假设手续费在卖出股票的时候才会扣除。
程序如下:
// 714、股票+手续费-动态规划
class Solution9 {
public:
int maxProfit(vector<int>& prices, int fee) {
vector<vector<int>> dp(prices.size(), vector<int>(2, 0)); // 二维数组,行代表天数,列代表是否持有股票
dp[0][0] -= prices[0];
for (int i = 1; i < prices.size(); i++) {
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]); // 第i天持有股票
dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + prices[i] - fee); // 第i天不持有股票
}
return dp[prices.size() - 1][1];
}
};
复杂度分析:
# include
# include
using namespace std;
// 121、股票I-动态规划
class Solution {
public:
int maxProfit(vector<int>& prices) {
if (prices.size() == 0) return 0;
vector<vector<int>> dp(prices.size(), vector<int>(2)); // 二维数组,行代表天数,列代表是否持有股票
dp[0][0] -= prices[0];
dp[0][1] = 0;
for (int i = 1; i < prices.size(); i++) {
dp[i][0] = max(dp[i - 1][0], -prices[i]); // 第i天持有股票
dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0]); // 第i天不持有股票
}
return dp[prices.size() - 1][1];
}
};
// 121、股票I-动态规划-滚动数组
class Solution2 {
public:
int maxProfit(vector<int>& prices) {
if (prices.size() == 0) return 0;
vector<vector<int>> dp(2, vector<int>(2)); // 二维数组,行代表天数,列代表是否持有股票
dp[0][0] -= prices[0];
dp[0][1] = 0;
for (int i = 1; i < prices.size(); i++) {
dp[i % 2][0] = max(dp[(i - 1) % 2][0], -prices[i]); // 第i天持有股票
dp[i % 2][1] = max(dp[(i - 1) % 2][1], prices[i] + dp[(i - 1) % 2][0]); // 第i天不持有股票
}
return dp[(prices.size() - 1) % 2][1];
}
};
// 122、股票II-动态规划
class Solution3 {
public:
int maxProfit(vector<int>& prices) {
if (prices.size() == 0) return 0;
vector<vector<int>> dp(prices.size(), vector<int>(2)); // 二维数组,行代表天数,列代表是否持有股票
dp[0][0] -= prices[0];
dp[0][1] = 0;
for (int i = 1; i < prices.size(); i++) {
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]); // 第i天持有股票
dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + prices[i]); // 第i天不持有股票
}
return dp[prices.size() - 1][1];
}
};
// 122、股票II-动态规划-滚动数组
class Solution4 {
public:
int maxProfit(vector<int>& prices) {
if (prices.size() == 0) return 0;
vector<vector<int>> dp(2, vector<int>(2)); // 二维数组,行代表天数,列代表是否持有股票
dp[0][0] -= prices[0];
dp[0][1] = 0;
for (int i = 1; i < prices.size(); i++) {
dp[i % 2][0] = max(dp[(i - 1) % 2][0], dp[(i - 1) % 2][1] - prices[i]); // 第i天持有股票
dp[i % 2][1] = max(dp[(i - 1) % 2][1], dp[(i - 1) % 2][0] + prices[i]); // 第i天不持有股票
}
return dp[(prices.size() - 1) % 2][1];
}
};
// 123、股票III-动态规划
class Solution5 {
public:
int maxProfit(vector<int>& prices) {
if (prices.size() == 0) return 0;
vector<vector<int>> dp(prices.size(), vector<int>(5)); // 二维数组,行代表天数,列代表不同状态
dp[0][0] = 0; // 第一天没有操作
dp[0][1] -= prices[0]; // 第一天第一次持有股票 = 第一天买入
dp[0][2] = 0; // 第一天第一次不持有股票 = 第一天买入,又卖出
dp[0][3] -= prices[0]; // 第一天第二次持有股票 = 第一天买入,卖出,再买入
dp[0][4] = 0; // 第一天第二次不持有股票 = 第一天买入卖出两次
for (int i = 1; i < prices.size(); i++) {
dp[i][0] = dp[i-1][0]; // 第i天没有操作
dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - prices[i]); // 第i天第一次持有股票
dp[i][2] = max(dp[i - 1][2], dp[i - 1][1] + prices[i]); // 第i天第一次不持有股票
dp[i][3] = max(dp[i - 1][3], dp[i - 1][2] - prices[i]); // 第i天第二次持有股票
dp[i][4] = max(dp[i - 1][4], dp[i - 1][3] + prices[i]); // 第i天第二次不持有股票
}
return dp[prices.size() - 1][4];
}
};
// 123、股票III-动态规划-滚动数组
class Solution6 {
public:
int maxProfit(vector<int>& prices) {
if (prices.size() == 0) return 0;
vector<int> dp(5, 0); // 行代表天数,列代表不同状态
dp[1] -= prices[0]; // 第一天第一次持有股票 = 第一天买入
dp[3] -= prices[0]; // 第一天第二次持有股票 = 第一天买入,卖出,再买入
for (int i = 1; i < prices.size(); i++) {
dp[1] = max(dp[1], dp[0] - prices[i]); // 第i天第一次持有股票
dp[2] = max(dp[2], dp[1] + prices[i]); // 第i天第一次不持有股票
dp[3] = max(dp[3], dp[2] - prices[i]); // 第i天第二次持有股票
dp[4] = max(dp[4], dp[3] + prices[i]); // 第i天第二次不持有股票
}
return dp[4];
}
};
// 188、股票IV-动态规划
class Solution7 {
public:
int maxProfit(int k, vector<int>& prices) {
vector<vector<int>> dp(prices.size(), vector<int>(2 * k + 1, 0)); // 二维数组,行代表天数,列代表不同状态
for (int i = 1; i < 2 * k; i += 2) {
dp[0][i] -= prices[0]; // 第一天第i次持有股票
}
for (int i = 1; i < prices.size(); i++) {
for (int j = 0; j < 2 * k - 1; j +=2) {
dp[i][j + 1] = max(dp[i - 1][j + 1], dp[i - 1][j] - prices[i]); // 第i天第j次持有股票
dp[i][j + 2] = max(dp[i - 1][j + 2], dp[i - 1][j + 1] + prices[i]); // 第i天第j次不持有股票
}
}
return dp[prices.size() - 1][2 * k];
}
};
// 309、股票+冷冻期-动态规划
class Solution8 {
public:
int maxProfit(vector<int>& prices) {
vector<vector<int>> dp(prices.size(), vector<int>(4, 0)); // 二维数组,行代表天数,列代表不同状态
dp[0][0] = -prices[0];
for (int i = 1; i < prices.size(); i++) {
dp[i][0] = max(dp[i - 1][0], max(dp[i - 1][1], dp[i - 1][3]) - prices[i]); // 状态1
dp[i][1] = max(dp[i - 1][1], dp[i - 1][3]); // 状态2
dp[i][2] = dp[i - 1][0] + prices[i]; // 状态3
dp[i][3] = dp[i - 1][2]; // 状态4
}
return max(dp[prices.size() - 1][1], max(dp[prices.size() - 1][2], dp[prices.size() - 1][3]));
}
};
// 714、股票+手续费-动态规划
class Solution9 {
public:
int maxProfit(vector<int>& prices, int fee) {
vector<vector<int>> dp(prices.size(), vector<int>(2, 0)); // 二维数组,行代表天数,列代表是否持有股票
dp[0][0] -= prices[0];
for (int i = 1; i < prices.size(); i++) {
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]); // 第i天持有股票
dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + prices[i] - fee); // 第i天不持有股票
}
return dp[prices.size() - 1][1];
}
};
int main() {
int k = 2;
int fee = 2;
vector<int> prices = { 7,1,5,3,6,4 };
//vector prices = { 3, 2, 6, 5, 0, 3 };
Solution9 s1;
int result = s1.maxProfit(prices, fee);
//int result = s1.maxProfit(k, prices);
//int result = s1.maxProfit(prices);
cout << result << endl;
system("pause");
return 0;
}
end