文章目录
- 1. 买卖股票的最佳时机含冷冻期(309)
- 2. 买卖股票的最佳时机含手续费(714)
- 3. 买卖股票的最佳时机 III(123)
- 4. 买卖股票的最佳时机 IV(188)
1. 买卖股票的最佳时机含冷冻期(309)
题目描述:
状态表示:
由题目和经验可知,本题可分为三种状态,分别是买入,可交易,以及冷冻期,因此分别建立f[i],g[i],h[i]来表示达到第i天结尾到达的状态所能够得到的最大利润。
状态转移方程:
第i天结束要到达买入状态,前一天结束到达状态可以是可交易状态以及买入状态,所以f[i]=max(g[i-1]-prices,f[i-1])。第i天结束要到达可交易状态,前一天结束到达状态可以是冷冻期以及可交易状态,所以g[i]=max(h[i-1],g[i-1])。同理,h[i]=max(f[i-1]+prices,g[i-1])。
初始化:
为了壁面越界要初始化f[0],g[0],h[0]。
填表顺序:
都是从左至右。
返回值:
f,h,g三个数组最后一个元素中的最大值。
代码如下:
class Solution {
public int maxProfit(int[] prices) {
int n = prices.length;
int[] f = new int[n];// 买入
int[] g = new int[n];// 可交易
int[] h = new int[n];// 冷冻期
f[0] = -prices[0];
for (int i = 1; i < n; i++) {
f[i] = Math.max(g[i - 1] - prices[i], f[i - 1]);
g[i] = Math.max(h[i - 1], g[i - 1]);
h[i] = Math.max(f[i - 1] + prices[i], g[i - 1]);
}
return Math.max(f[n - 1], Math.max(g[n - 1], h[n - 1]));
}
}
题目链接
时间复杂度:O(n)
空间复杂度:O(n)
2. 买卖股票的最佳时机含手续费(714)
题目描述:
状态表示:
由题目要求和经验可得此题由两种状态买入以及可交易,因此设置两个数组f[i],g[i]分别表示i位置该状态能获得的最大利润。
状态转移方程:
第i天结束时是买入状态,那么前一天结束可能是买入状态或者是可交易状态,f[i]=max(f[i-1],g[i-1]-price-fee)。第i天结束时是可交易状态,那么前一天结束可能是可交易状态或者是买入状态,g[i]=max(g[i-1],f[i-1]+price)。
初始化:
为了防止越界需要初始化f[0]以及g[0],这里初始化要注意f[0]需要减去一个fee,因为从逻辑上这里也算一次交易的开始所以需要减去一个fee,因为在这里的代码中我是选择在买入股票时减去fee的。
填表顺序:
都是从左至右。
返回值:
max(f[n-1],g[n-1])。
代码如下:
class Solution {
public int maxProfit(int[] prices, int fee) {
int n = prices.length;
int[] f = new int[n];
int[] g = new int[n];
f[0] = -prices[0] - fee;
for (int i = 1; i < n; i++) {
f[i] = Math.max(f[i - 1], g[i - 1] - prices[i] - fee);
g[i] = Math.max(g[i - 1], f[i - 1] + prices[i]);
}
return Math.max(f[n - 1], g[n - 1]);
}
}
题目链接
时间复杂度:O(n)
空间复杂度:O(n)
3. 买卖股票的最佳时机 III(123)
题目描述:
状态表示:
这题和上一题的状态表示一致,f[i]和g[i]分别表示第i天结束时到达买入以及可交易状态,但是要加上一个维度去表示交易次数,这里设置当卖出股票时交易完成一次即交易次数加一。
状态转移方程:
状态转移方程和上题类似因此这里直接给出f[i][j]=max(f[i-1][j],g[i-1][j]-prices),g[i][j]=max(g[i-1][j],f[i-1][j-1]+price),这里的j-1当j=0时就会出问题,所以先将g[i][j]赋值为g[i-1][j],再判断j的大小,如果j合理则g[i][j]=max(g[i][j],f[i-1][j-1]+price)。
初始化:
初始化f[0][j]以及g[0][j],因为这里的交易次数有限所以逻辑上要尽量节省交易次数避免在同一天多次买入和卖出,为了避免影响要将f[0][1],f[0][2]赋为无穷小,g[0][1]和g[0][2]也是一样。
填表顺序:
从左至右,从上至下。
返回值:
返回g[i][j]最后一排的最大值,因为逻辑上f表示买入,既然手里还有股票就不可能达到最大利润。
代码如下:
class Solution {
public int maxProfit(int[] prices) {
int n = prices.length;
int[][] f = new int[n][3];
int[][] g = new int[n][3];
f[0][0] = -prices[0];
f[0][1] = -0x3f3f3f;
f[0][2] = -0x3f3f3f;
g[0][1] = -0x3f3f3f;
g[0][2] = -0x3f3f3f;
for (int i = 1; i < n; i++) {
for (int j = 0; j < 3; j++) {
f[i][j] = Math.max(f[i - 1][j], g[i - 1][j] - prices[i]);
g[i][j] = g[i - 1][j];
if (j - 1 >= 0) {
g[i][j] = Math.max(g[i][j], f[i - 1][j - 1] + prices[i]);
}
}
}
int max = 0;
for (int i = 0; i < 3; i++) {
if (g[n - 1][i] > max) {
max = g[n - 1][i];
}
}
return max;
}
}
题目链接
时间复杂度:O(n)
空间复杂度:O(n)
4. 买卖股票的最佳时机 IV(188)
题目描述:
题目解析:
这题就是将上题的最多2次交易换成了k,代码照着写即可,不过要注意一个细节,如果交易次数太大要将交易次数减为prices数组长度的一半提高效率。
代码如下:
public int maxProfit(int k, int[] prices) {
int n = prices.length;
k = Math.min(n / 2, k);
int[][] f = new int[n][k + 1];
int[][] g = new int[n][k + 1];
f[0][0] = -prices[0];
for (int i = 1; i <= k; i++) {
f[0][i] = -0x3f3f3f;
g[0][i] = -0x3f3f3f;
}
for (int i = 1; i < n; i++) {
for (int j = 0; j <= k; j++) {
f[i][j] = Math.max(f[i - 1][j], g[i - 1][j] - prices[i]);
g[i][j] = g[i - 1][j];
if (j - 1 >= 0) {
g[i][j] = Math.max(g[i][j], g[i - 1][j] + prices[i]);
}
}
}
int max = 0;
for (int i = 0; i <= k; i++) {
max = Math.max(max, g[n - 1][i]);
}
return max;
}
题目链接
时间复杂度:O(n^2)
空间复杂度:O(n^2)