1、分析
使用通用方法,也即动态规划DP
(1)LeetCode 121. 买卖股票的最佳时机
class Solution {
public int maxProfit(int[] prices) {
if(prices == null || prices.length == 0) return 0;
int n = prices.length;
int[][] dp = new int[n][2];
for(int i = 0;i < n;i ++) {
if(i - 1 == -1) {
dp[i][0] = 0;
dp[i][1] = -prices[i];
continue;
}
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[n-1][0];
}
}
(2)LeetCode 122. 买卖股票的最佳时机 II
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
表示k为正无穷:k 与 k-1 近似相等,因此每个式子都有k,可以不用考虑。
class Solution {
public int maxProfit(int[] prices) {
if(prices == null || prices.length == 0) return 0;
//k为正无穷
final int INF = (int)1e9;
int n = prices.length;
int[][] dp = new int[n][2];
for(int i = 0;i < n;i ++) {
if(i - 1 == -1) {
dp[i][0] = 0;
dp[i][1] = -prices[i];
continue;
}
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[n-1][0];
}
}
(3)LeetCode 123. 买卖股票的最佳时机 III
设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。
K <= 2
这里因为 k要取到2,所以dp数组中开的空间为3
class Solution {
public int maxProfit(int[] prices) {
/*
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])
base case:
dp[-1][k][0] = dp[i][0][0] = 0;
dp[-1][k][1] = dp[i][0][1] = -INF
*/
if(prices == null || prices.length == 0) return 0;
int n = prices.length;
int K = 2;
int[][][] dp = new int[n][K+1][2];
for(int i = 0;i < n;i ++) {
for(int k = 1;k <= K;k ++) {
if(i - 1 == -1) {
dp[i][k][0] = 0;
dp[i][k][1] = -prices[i];
continue;
}
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]);
}
}
return dp[n-1][K][0];
}
}
(4)LeetCode 188. 买卖股票的最佳时机 IV
设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。
k应该满足一个限制条件,即 k < n / 2,因为两天进行一次交易;
若k > n / 2,超出这个限制,则转到第二种情况,k可以取到正无穷次。
这里因为 k要取到2,所以dp数组中开的空间为3
class Solution {
public int maxProfit(int k, int[] prices) {
if(prices == null || prices.length < 2) return 0;
int n = prices.length;
//k的取值应该保证在一个范围内
if(k > n / 2) {
return maxProfitWithOutK(prices, n);
}
//k <= n / 2
int[][][] dp = new int[n][k+1][2];
for(int i = 0;i < n;i ++) {
for(int j = 1;j <= k;j ++) {
if(i - 1 == -1) {
dp[i][j][0] = 0;
dp[i][j][1] = -prices[i];
continue;
}
dp[i][j][0] = Math.max(dp[i-1][j][0], dp[i-1][j][1] + prices[i]);
dp[i][j][1] = Math.max(dp[i-1][j][1], dp[i-1][j-1][0] - prices[i]);
}
}
return dp[n-1][k][0];
}
int maxProfitWithOutK(int[] prices, int n) {
int[][] f = new int[n][2];
//初始化
f[0][1] = -prices[0];
for(int i = 1;i < n;i ++) {
f[i][0] = Math.max(f[i-1][0], f[i-1][1] + prices[i]);
f[i][1] = Math.max(f[i-1][1], f[i-1][0] - prices[i]);
}
return f[n-1][0];
}
}
(5)LeetCode 309. 最佳买卖股票时机含冷冻期
卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
当天若买入持有了股票,一定从前一天冷冻期的状态转移过来的。
class Solution {
public int maxProfit(int[] prices) {
if(prices == null || prices.length <= 1) return 0;
int n = prices.length;
//dp[i][k][3] k为尽可能大,所以不需要考虑
/*
dp[i][0] 代表第i天不持有股票
dp[i][1] 代表第i天持有股票
dp[i][2] 代表第i天处于冷冻期
*/
int[][] dp = new int[n][3];
//初始化
dp[0][1] = -prices[0];
//dp[0][2] = 0;
for(int i = 1;i < n;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][2] - prices[i]); //前一天处于冷冻期
dp[i][2] = dp[i-1][0]; //前一天卖出股票
}
/*for(int i = 0;i < n;i ++) {
if(i - 1 == -1) {
dp[i][0] = 0;
dp[i][1] = -prices[i];
continue;
}
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][2] - prices[i]);
dp[i][2] = dp[i-1][0]; //前一天卖出股票
} */
return dp[n-1][0];
}
}
(6)LeetCode 714. 买卖股票的最佳时机含手续费
你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。
在卖出股票扣手续费即可。
class Solution {
public int maxProfit(int[] prices, int fee) {
if(prices == null || prices.length < 2) return 0;
int n = prices.length;
int[][] dp = new int[n][2];
for(int i = 0;i < n;i ++) {
if(i - 1 == -1) {
dp[i][0] = 0;
dp[i][1] = -prices[i];
continue;
}
dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1] + prices[i] - fee); //在卖出股票扣手续费
dp[i][1] = Math.max(dp[i-1][1], dp[i-1][0] - prices[i]);
}
return dp[n-1][0];
}
}