Leetcode Stock 系列

原博

因为所关心的是利润,所以以利润为状态。

一,一般情况分析

以T[i][k]来表示在第i天实现了k次交易得到的利润。所以当给定i个price,和最大k次交易的时候,所得T[i][k]即是结果。如此定义我们有,T[-1][k] = T[i][0] = 0 (第一天之前和未交易时所得利润均为0)。

那么T[i][k]的变化与什么有关呢?

一个人当天的操作只有三种,buy,rest,sell。在这个系列问题的设定下,一个人手上最多只能同时持一股。所以在一个人做完当天的操作之后,T只有两种状态:

  • 手上0股所得利润T[i][k][0]。
  • 手上1股所得利润T[i][k][1]。

下面来看看T是如何变化的:

当天交易完T[i][k][0]和什么有关呢?0表示操作完手上没有股票了,这有可能是今天卖出或者是之前卖出而今天rest得到的结果,可以写出T[i][k][0]的变化式:

T[i][k][0] = max(T[i-1][k][1] + prices[i], T[i-1][k][0])
  • 第一项的意思是,今天之前手上有一股,而我在今天选择卖出,所以今天会收入对应股价的利润,注意由于T表示利润,所以成本已经减掉了,新的利润直接加上股价即可,由于卖出不影响交易数量,所以交易数量保持k不变
  • 第二项的意思是,今天之前我手上一股也没有,但我今天选择佛系,不买入,所以利润不变。

当天交易完T[i][k][1]和什么有关呢?1表示操作完手上还有一股,那么这一股有可能是今天买入或者是之前买入而今天rest得到的结果,可以写出T[i][k][1]的变化式:

T[i][k][1] = max(T[i-1][k-1][0] - prices[i], T[i-1][k][1])
  • 第一项的意思是,今天之前我的手上没有股票,今天购入了一股,证明我今天是在已得利润上花钱买了一股,所以是利润减今天的股价,我的交易数量也因为买入一股而从k-1增长到k
  • 第二项的意思是,今天我选择不买不卖,所以利润没有变化,我的交易数量也没有变化。

遍历股价,最终得到的结果T[i][k][0]即是我们需要的最大利润。

二,特殊情况分析

其实各个题型的变化实际在变的内容都是交易数量,或是在交易的内容中加入一些特殊的变化,由此产生了leetcode中的6个Stock系列的题目。所以接下来我们看看一般分析是怎样化用到Stock系列的题型中的。

case 1:只交易一次 (k=1),对应leetcode第121题 Best Time to Buy and Sell Stock

T[i][k][0] = max(T[i-1][k][1] + prices[i], T[i-1][k][0])   ===>  T[i][1][0] = max(T[i-1][1][1] + prices[i], T[i-1][1][0]) 

T[i][k][1] = max(T[i-1][k-1][0] - prices[i], T[i-1][k][1]) ===>  T[i][1][1] = max(T[i-1][0][0] - prices[i], T[i-1][1][1]) ===> T[i][1][1] = max(-prices[i], T[i-1][1][1]) (因为T[i-1][0][0] = 0)

翻译成代码:

int maxProfit(vector<int>& prices) {
    int T_i10 = 0, T_i11 = INT_MIN;
        
    for (auto price : prices) {
        T_i10 = max(T_i10, T_i11 + price);
        T_i11 = max(T_i11, -price);
    }
        
    return T_i10;
}

case 2:可交易无数次,对应leetcode第122题 Best Time to Buy and Sell Stock II

当交易次数为无数次时,k就不会明显地影响到利润了,所得最大利润更多的取决于可供交易的股价数量。

原解释如下:

Sorry for the confusion. So there are two interpretations here. First from a mathematical point of view, limit(k) is the same as limit(k-1) when k approaches +infinitySecond, more relevant to this problem, as I said for the case of arbitrary k, when k is sufficiently large, the maximum profits will on longer depend on k but be bound by the number of available stocks. This means if k goes to +infinity, the profits won't change if you increase or decrease the value of k, that is, T[i][k][0] will be the same as T[i][k-1][0] and T[i][k][1] the same as T[i][k-1][1]. Hope this clarifies things a little bit

变化式变为:

T[i][k][0] = max(T[i-1][k][1] + prices[i], T[i-1][k][0])   ===>  T[i][k][0] = max(T[i-1][k][1] + prices[i], T[i-1][k][0]) 

T[i][k][1] = max(T[i-1][k-1][0] - prices[i], T[i-1][k][1]) ===>  T[i][k][1] = max(T[i-1][k][0] - prices[i], T[i-1][k][1])

翻译成代码:

int maxProfit(vector<int>& prices) {
    int T_ik0 = 0, T_ik1 = INT_MIN;
    
    for (auto price : prices) {
        int T_ik0_old = T_ik0;
        T_ik0 = max(T_ik0, T_ik1 + price);
        T_ik1 = max(T_ik1, T_ik0_old - price);
    }
    
    return T_ik0;
}

case 3:可交易规定次数,对应leetcode第123和124题 Best Time to Buy and Sell Stock III, Best Time to Buy and Sell Stock IV

与交易无数次不同,这种题型是交易有限次数,k对最大利润的影响需要考虑,所以之前将k-1简化为k的行为不可以再次出现啦。当然这次的式子变化也是最朴素的,如下:

T[i][k][0] = max(T[i-1][k][1] + prices[i], T[i-1][k][0])

T[i][k][1] = max(T[i-1][k-1][0] - prices[i], T[i-1][k][1])

就,没变,贴在这只不过不需要翻回去了,,,

翻译成代码:

int maxProfit(int k, vector<int>& prices) {
    if (prices.empty()) return 0;
    vector<int> T_ik0(k + 1);
    vector<int> T_ik1(k + 1, INT_MIN);
        
    for (auto price : prices) {
        for (int j = k; j > 0; j--) {
            T_ik0[j] = max(T_ik0[j], T_ik1[j] + price);
            T_ik1[j] = max(T_ik1[j], T_ik0[j - 1] - price);
        }
    }
        
    return T_ik0[k];
}

case 4:可交易无数次,但是有cool down,对应leetcode第309题,Best Time to Buy and Sell Stock with Cooldown

这一题就是case2的变种,所以变化式会在case2的基础上有略微的改变

T[i][k][0] = max(T[i-1][k][1] + prices[i], T[i-1][k][0])   ===>  T[i][k][0] = max(T[i-1][k][1] + prices[i], T[i-1][k][0]) 

T[i][k][1] = max(T[i-1][k-1][0] - prices[i], T[i-1][k][1]) ===>  T[i][k][1] = max(T[i-1][k][0] - prices[i], T[i-1][k][1]) ===>  T[i][k][1] = max(T[i-2][k][0] - prices[i], T[i-1][k][1]) (因为cool down)

翻译成代码:

int maxProfit(vector<int>& prices) {
    int T_ik0_pre = 0, T_ik0 = 0, T_ik1 = INT_MIN;
    
    for (int price : prices) {
        int T_ik0_old = T_ik0;
        T_ik0 = max(T_ik0, T_ik1 + price);
        T_ik1 = max(T_ik1, T_ik0_pre - price);
        T_ik0_pre = T_ik0_old;
    }
    
    return T_ik0;
}

case 5:可交易无数次,但是有transaction fee,对应leetcode第714题,Best Time to Buy and Sell Stock with Transaction Fee

这一题也是case2的变种,所以变化式会在case2的基础上有略微的改变,交易费可以在买入的时候减去,也可以在卖出的时候减去,这样变化后的式子就有两种情况:

买入时减fee

T[i][k][0] = max(T[i-1][k][1] + prices[i], T[i-1][k][0])   ===>  T[i][k][0] = max(T[i-1][k][1] + prices[i], T[i-1][k][0]) 

T[i][k][1] = max(T[i-1][k-1][0] - prices[i], T[i-1][k][1]) ===>  T[i][k][1] = max(T[i-1][k][0] - prices[i] - fee, T[i-1][k][1])

卖出时减fee

T[i][k][0] = max(T[i-1][k][1] + prices[i], T[i-1][k][0])   ===>  T[i][k][0] = max(T[i-1][k][1] + prices[i] - fee, T[i-1][k][0]) 

T[i][k][1] = max(T[i-1][k-1][0] - prices[i], T[i-1][k][1]) ===>  T[i][k][1] = max(T[i-1][k][0] - prices[i], T[i-1][k][1])

翻译成代码:

买入时减fee

int maxProfit(vector<int>& prices, int fee) {
    int T_ik0 = 0, T_ik1 = INT_MIN;
    
    for (auto price : prices) {
        int T_ik0_old = T_ik0;
        T_ik0 = max(T_ik0, T_ik1 + price);
        T_ik1 = max(T_ik1, T_ik0_old - price - fee);
    }
        
    return T_ik0;
}

卖出时减fee

int maxProfit(vector<int>& prices, int fee) {
    if (prices.empty()) return 0;
    int T_ik0 = 0, T_ik1 = -prices[0];
    
    for (auto price : prices) {
        int T_ik0_old = T_ik0;
        T_ik0 = max(T_ik0, T_ik1 + price - fee);
        T_ik1 = max(T_ik1, T_ik0_old - price);
    }
        
    return T_ik0;
}

三、结论

懒得写了哈哈哈。

In summary, the most general case of the stock problem can be characterized by three factors, the ordinal of the day i, the maximum number of allowable transactions k, and the number of stocks in our hand at the end of the day. I have shown the recurrence relations for the maximum profits and their termination conditions, which leads to the O(nk) time and O(k) space solution.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值