LeetCode上买卖股票的问题如下:
可以统一为同一种dp状态,可以用T[i][k]表示第i天为止最多k次交易最大利润,因为当天交易状态最后有两种:一种是持有股票,另一种是没有股票,因此可以细分为两种状态:T[i][k][0],T[i][k][1]
状态转移方程为:
T[i][k][0] = max(T[i-1][k][0], T[i-1][k][1] + prices[i])
T[i][k][1] = max(T[i-1][k][1], T[i-1][k-1][0] - prices[i])
把状态归纳为:第i天完成最多k次交易,0表示手上没有股票,1表示有:这里有没有股票的状态具有一般性,实际上就是dp状态跟当前值有没有关系
上述六个股票问题可以按k的值进行分类,k是允许交易的最大数量(最后两个相当于加了额外条件,如“冷却时间”或“交易费”)。
121. Best Time to Buy and Sell Stock
k=1的情况,
T[i][1][0] = max(T[i-1][1][0], T[i-1][1][1] + prices[i])
T[i][1][1] = max(T[i-1][1][1], T[i-1][0][0] - prices[i]) = max(T[i-1][1][1], -prices[i])
可以进一步优化为两个变量:
public int maxProfit(int[] prices) {
int T_i10 = 0, T_i11 = Integer.MIN_VALUE;
for (int price : prices) {
T_i10 = Math.max(T_i10, T_i11 + price);
T_i11 = Math.max(T_i11, -price);
}
return T_i10;
}
122. Best Time to Buy and Sell Stock II
k=oo,意味着k和k-1状态一样
T[i][k][0] = max(T[i-1][k][0], T[i-1][k][1] + prices[i])
T[i][k][1] = max(T[i-1][k][1], T[i-1][k-1][0] - prices[i]) = max(T[i-1][k][1], T[i-1][k][0] - prices[i])
同样可以优化空间:
public int maxProfit(int[] prices) {
int T_ik0 = 0, T_ik1 = Integer.MIN_VALUE;
for (int price : prices) {
int T_ik0_old = T_ik0;
T_ik0 = Math.max(T_ik0, T_ik1 + price);
T_ik1 = Math.max(T_ik1, T_ik0_old - price);
}
return T_ik0;
}
123. Best Time to Buy and Sell Stock III
k=2的情况:T[i][1][0]
, T[i][1][1]
, T[i][2][0]
, T[i][2][1]可以直接用4个变量表示
T[i][2][0] = max(T[i-1][2][0], T[i-1][2][1] + prices[i])
T[i][2][1] = max(T[i-1][2][1], T[i-1][1][0] - prices[i])
T[i][1][0] = max(T[i-1][1][0], T[i-1][1][1] + prices[i])
T[i][1][1] = max(T[i-1][1][1], -prices[i])
public int maxProfit(int[] prices) {
int T_i10 = 0, T_i11 = Integer.MIN_VALUE;
int T_i20 = 0, T_i21 = Integer.MIN_VALUE;
for (int price : prices) {
T_i20 = Math.max(T_i20, T_i21 + price);
T_i21 = Math.max(T_i21, T_i10 - price);
T_i10 = Math.max(T_i10, T_i11 + price);
T_i11 = Math.max(T_i11, -price);
}
return T_i20;
}
188. Best Time to Buy and Sell Stock IV
k为任意值,这是最常见的情况,因此每天我们需要在当天结束时用对应于0或1只股票的不同k值更新所有最大利润。但是,如果k超过某个临界值,我们可以做一些小的优化,超过这个值,最大利润不会长期取决于允许的交易数量,而是受可用股票数量的限制(价格数组的长度)
public int maxProfit(int k, int[] prices) {
if (k >= prices.length >>> 1) {
int T_ik0 = 0, T_ik1 = Integer.MIN_VALUE;
for (int price : prices) {
int T_ik0_old = T_ik0;
T_ik0 = Math.max(T_ik0, T_ik1 + price);
T_ik1 = Math.max(T_ik1, T_ik0_old - price);
}
return T_ik0;
}
int[] T_ik0 = new int[k + 1];
int[] T_ik1 = new int[k + 1];
Arrays.fill(T_ik1, Integer.MIN_VALUE);
for (int price : prices) {
for (int j = k; j > 0; j--) {
T_ik0[j] = Math.max(T_ik0[j], T_ik1[j] + price);
T_ik1[j] = Math.max(T_ik1[j], T_ik0[j - 1] - price);
}
}
return T_ik0[k];
}
309. Best Time to Buy and Sell Stock with Cooldown.
k = +Infinity but with cooldown
没有冷却的转移情况是:
T[i][k][0] = max(T[i-1][k][0], T[i-1][k][1] + prices[i])
T[i][k][1] = max(T[i-1][k][1], T[i-1][k][0] - prices[i])
但是在“冷却时间”下,如果在第(i-1)天出售股票,我们就无法在第i天购买。因此,在上面的第二个等式中,如果我们打算在第i天购买,我们实际上应该使用T [i-2] [k] [0]而不是T [i-1] [k] [0]。这里冷冻期是在买之前,所以卖的时候不用考虑,第二个等式才要考虑:注意虽然这里T[i-1][k][0]不一定在i-1天有卖股票但是这种情况也适合于T[i-2][k][0],也就是i-2天包含了i-1中不卖股票的情况所以是正确的
T[i][k][0] = max(T[i-1][k][0], T[i-1][k][1] + prices[i])
T[i][k][1] = max(T[i-1][k][1], T[i-2][k][0] - prices[i])
public int maxProfit(int[] prices) {
int T_ik0_pre = 0, T_ik0 = 0, T_ik1 = Integer.MIN_VALUE;
for (int price : prices) {
int T_ik0_old = T_ik0;
T_ik0 = Math.max(T_ik0, T_ik1 + price);
T_ik1 = Math.max(T_ik1, T_ik0_pre - price);
T_ik0_pre = T_ik0_old;
}
return T_ik0;
}
714. Best Time to Buy and Sell Stock with Transaction Fee
k = +Infinity but with transaction fee
同样,这个同情况2一样,因为它们具有相同的k值,除了现在需要稍微修改递归关系以考虑“交易费”要求。案例II的原始复发关系由下式给出
T[i][k][0] = max(T[i-1][k][0], T[i-1][k][1] + prices[i])
T[i][k][1] = max(T[i-1][k][1], T[i-1][k][0] - prices[i])
由于现在我们需要为每笔交易支付一些费用(表示为费用),因此在第i天购买或出售股票后的利润应减去这个数额,因此新的递推关系:
T[i][k][0] = max(T[i-1][k][0], T[i-1][k][1] + prices[i])
T[i][k][1] = max(T[i-1][k][1], T[i-1][k][0] - prices[i] - fee)
或者
T[i][k][0] = max(T[i-1][k][0], T[i-1][k][1] + prices[i] - fee)
T[i][k][1] = max(T[i-1][k][1], T[i-1][k][0] - prices[i])
public int maxProfit(int[] prices, int fee) {
int T_ik0 = 0, T_ik1 = Integer.MIN_VALUE;
for (int price : prices) {
int T_ik0_old = T_ik0;
T_ik0 = Math.max(T_ik0, T_ik1 + price);
T_ik1 = Math.max(T_ik1, T_ik0_old - price - fee);
}
return T_ik0;
}