Note: this is a repost(重新投寄) of my original post here with updated solutions(解决方案) for this problem (714. Best Time to Buy and Sell Stock with Transaction Fee). If you are only looking for solutions, you can go directly to each section in part II — Applications to specific(特殊的) cases.

Up to this point, I believe you have finished the following series of stock problems:

  • Best Time to Buy and Sell Stock
  • Best Time to Buy and Sell Stock II
  • Best Time to Buy and Sell Stock III
  • Best Time to Buy and Sell Stock IV
  • Best Time to Buy and Sell Stock with Cooldown
  • Best Time to Buy and Sell Stock with Transaction(交易) Fee

For each problem, we’ve got a couple of excellent posts explaining how to approach(接近) it. However, most of the posts failed to identify(确定) the connections among these problems and made it hard to develop a consistent(始终如一的) way of dealing with this series of problems. Here I will introduce the most generalized(广义的) solution(解决方案) applicable(可适用的) to all of these problems, and its specialization(专门化) to each of the six problems above.

I — General cases

The idea begins with the following question: Given an array(数组) representing the price of stock on each day, what determines the maximum profit(利润) we can obtain?

Most of you can quickly come up with answers like “it depends on which day we are and how many transactions we are allowed to complete”. Sure, those are important factors(因素) as they manifest(证明) themselves in the problem descriptions. However, there is a hidden factor that is not so obvious but vital(至关重要的) in determining the maximum profit, which is elaborated(精心制作) below.

First let’s spell out the notations(符号) to streamline(合理化) our analyses(分析). Let prices be the stock price array with length n, i denote the i-th day (i will go from 0 to n-1), k denote(表示) the maximum number of transactions(交易) allowed to complete, T[i][k] be the maximum profit(利润) that could be gained at the end of the i-th day with at most k transactions. Apparently(显然的) we have base cases: T[-1][k] = T[i][0] = 0, that is, no stock or no transaction yield(屈服) no profit (note the first day has i = 0 so i = -1 means no stock). Now if we can somehow relate T[i][k] to its subproblems(子问题) like T[i-1][k], T[i][k-1], T[i-1][k-1], …, we will have a working recurrence(再发生) relation and the problem can be solved recursively(递归的). So how do we achieve that?

The most straightforward(简单的) way would be looking at actions taken on the i-th day. How many options do we have? The answer is three: buy, sell, rest. Which one should we take? The answer is: we don’t really know, but to find out which one is easy. We can try each option and then choose the one that maximizes(取…最大值) our profit(利润), provided there are no other restrictions(限制). However, we do have an extra restriction saying no multiple transactions(交易) are allowed at the same time, meaning if we decide to buy on the i-th day, there should be 0 stock held in our hand before we buy; if we decide to sell on the i-th day, there should be exactly 1 stock held in our hand before we sell. The number of stocks held in our hand is the hidden factor(因素) mentioned above that will affect the action on the i-th day and thus affect the maximum profit(利润).

Therefore our definition(定义) of T[i][k] should really be split into two: T[i][k][0] and T[i][k][1], where the former denotes(表示) the maximum profit at the end of the i-th day with at most k transactions and with 0 stock in our hand AFTER taking the action, while the latter denotes(表示) the maximum profit(利润) at the end of the i-th day with at most k transactions and with 1 stock in our hand AFTER taking the action. Now the base cases and the recurrence(再发生) relations can be written as:

Base cases:
T[-1][k][0] = 0, T[-1][k][1] = -Infinity
T[i][0][0] = 0, T[i][0][1] = -Infinity Recurrence relation:
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])

For the base cases, T[-1][k][0] = T[i][0][0] = 0 has the same meaning as before while T[-1][k][1] = T[i][0][1] = -Infinity emphasizes(强调) the fact that it is impossible for us to have 1 stock in hand if there is no stock available or no transactions(交易) are allowed.

For T[i][k][0] in the recurrence(再发生) relations, the actions taken on the i-th day can only be rest and sell, since we have 0 stock in our hand at the end of the day. T[i-1][k][0] is the maximum profit(利润) if action rest is taken, while T[i-1][k][1] + prices[i] is the maximum profit if action sell is taken. Note that the maximum number of allowable(许可的) transactions(交易) remains the same, due to the fact that a transaction consists of two actions coming as a pair – buy and sell. Only action buy will change the maximum number of transactions(交易) allowed (well, there is actually an alternative(供选择的) interpretation(解释), see my comment below).

For T[i][k][1] in the recurrence(再发生) relations, the actions taken on the i-th day can only be rest and buy, since we have 1 stock in our hand at the end of the day. T[i-1][k][1] is the maximum profit(利润) if action rest is taken, while T[i-1][k-1][0] – prices[i] is the maximum profit(利润) if action buy is taken. Note that the maximum number of allowable(许可的) transactions(交易) decreases(减少) by one, since buying on the i-th day will use one transaction, as explained above.

To find the maximum profit at the end of the last day, we can simply loop(打环) through the prices array and update T[i][k][0] and T[i][k][1] according to the recurrence(再发生) relations above. The final answer will be T[i][k][0] (we always have larger profit if we end up with 0 stock in hand).

II — Applications to specific(特殊的) cases

The aforementioned(上述的) six stock problems are classified(分类) by the value of k, which is the maximum number of allowable(许可的) transactions(交易) (the last two also have additional(附加的) requirements such as “cooldown” or “transaction fee”). I will apply the general solution(解决方案) to each of them one by one.

Case I: k = 1

For this case, we really have two unknown variables(变量) on each day: T[i][1][0] and T[i][1][1], and the recurrence(再发生) relations say:

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])

where we have taken advantage of the base caseT[i][0][0] = 0 for the second equation(方程式).

It is straightforward(简单的) to write the O(n) time and O(n) space solution, based on the two equations above. However, if you notice that the maximum profits(利润) on the i-th day actually only depend on those on the (i-1)-th day, the space can be cut down to O(1). Here is the space-optimized solution(解决方案):

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;
}

Now let’s try to gain some insight(洞察力) of the solution above. If we examine the part inside the loop(环) more carefully, T_i11 really just represents the maximum value of the negative(否定) of all stock prices up to the i-th day, or equivalently(等价的) the minimum(最小的) value of all the stock prices. As for T_i10, we just need to decide which action yields(产量) a higher profit(利润), sell or rest. And if action sell is taken, the price at which we bought the stock is T_i11, i.e., the minimum value before the i-th day. This is exactly what we would do in reality if we want to gain maximum profit. I should point out that this is not the only way of solving the problem for this case. You may find some other nice solutions here.

Case II: k = +Infinity

If k is positive(积极的) infinity(无穷), then there isn’t really any difference between k and k – 1 (wonder why? see my comment below), which implies T[i-1][k-1][0] = T[i-1][k][0] and T[i-1][k-1][1] = T[i-1][k][1]. Therefore, we still have two unknown variables(变量) on each day: T[i][k][0] and T[i][k][1] with k = +Infinity, and the recurrence(再发生) relations say:

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])

where we have taken advantage of the fact that T[i-1][k-1][0] = T[i-1][k][0] for the second equation(方程式). The O(n) time and O(1) space solution(解决方案) is as follows:

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;
}

(Note: The caching of the old values of T_ik0, that is, the variable(变量的) T_ik0_old, is unnecessary. Special thanks to 0x0101 and elvina for clarifying this.)

This solution(解决方案) suggests a greedy strategy(战略) of gaining maximum profit(利润): as long as possible, buy stock at each local minimum(最小值) and sell at the immediately followed local maximum. This is equivalent(等价的) to finding increasing subarrays(子阵列) in prices (the stock price array(数组)), and buying at the beginning price of each subarray while selling at its end price. It’s easy to show that this is the same as accumulating(累积) profits as long as it is profitable(有利可图的) to do so, as demonstrated(证明) in this post.

Case III: k = 2

Similar to the case where k = 1, except now we have four variables(变量) instead of two on each day: T[i][1][0], T[i][1][1], T[i][2][0], T[i][2][1], and the recurrence(再发生) relations are:

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])

where again we have taken advantage of the base caseT[i][0][0] = 0 for the last equation(方程式). The O(n) time and O(1) space solution(解决方案) is as follows:

public int maxProfit(int[] prices) {
int T_i10 = 0, T_i11 = Integer.MIN_VALUE, 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;
}

which is essentially the same as the one given here.

Case IV: k is arbitrary

This is the most general case so on each day we need to update all the maximum profits(利润) with different k values corresponding to 0 or 1 stocks in hand at the end of the day. However, there is a minor(未成年的) optimization(最佳化) we can do if k exceeds(超过) some critical(鉴定的) value, beyond which the maximum profit(利润) will no long depend on the number of allowable(许可的) transactions(交易) but instead will be bound by the number of available stocks (length of the prices array(数组)). Let’s figure out what this critical value will be.

A profitable(有利可图的) transaction takes at least two days (buy at one day and sell at the other, provided the buying price is less than the selling price). If the length of the prices array is n, the maximum number of profitable transactions is n/2 (integer(整数) division). After that no profitable transaction is possible, which implies(意味) the maximum profit will stay the same. Therefore the critical value of k is n/2. If the given k is no less than this value, i.e., k >= n/2, we can extend(延伸) k to positive(积极的) infinity(无穷) and the problem is equivalent(等价的) to Case II.

The following is the O(kn) time and O(k) space solution(解决方案). Without the optimization(最佳化), the code will be met with TLE for large k values.

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];
}

The solution is similar to the one found in this post. Here I used backward looping(打环) for the T array(数组) to avoid using temporary(暂时的) variables(变量). It turns out that it is possible to do forward looping without temporary variables, too.

Case V: k = +Infinity but with cooldown

This case resembles Case II very much due to the fact that they have the same k value, except now the recurrence(再发生) relations have to be modified(修改) slightly to account for the “cooldown” requirement. The original recurrence relations for Case II are given by

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])

But with “cooldown”, we cannot buy on the i-th day if a stock is sold on the (i-1)-th day. Therefore, in the second equation(方程式) above, instead of T[i-1][k][0], we should actually use T[i-2][k][0] if we intend to buy on the i-th day. Everything else remains the same and the new recurrence(再发生) relations are

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])

And here is the O(n) time and O(1) space solution:

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;
}

dietpepsi shared a very nice solution(解决方案) here with thinking process, which turns out to be the same as the one above.

Case VI: k = +Infinity but with transaction(交易) fee

Again this case resembles(类似) Case II very much as they have the same k value, except now the recurrence(再发生) relations need to be modified(修改) slightly to account for the “transaction fee” requirement. The original recurrence relations for Case II are given by

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])

Since now we need to pay some fee (denoted(表示) as fee) for each transaction(交易) made, the profit(利润) after buying or selling the stock on the i-th day should be subtracted(减去) by this amount(总计), therefore the new recurrence relations will be either

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)

or

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])

Note we have two options as for when to subtract the fee. This is because (as I mentioned above) each transaction(交易) is characterized(以…为特点的) by two actions coming as a pair – buy and sell. The fee can be paid either when we buy the stock (corresponds to the first set of equations(方程式)) or when we sell it (corresponds to the second set of equations). The following are the O(n) time and O(1) space solutions(解决方案) corresponding to these two options, where for the second solution we need to pay attention to possible overflows(充满).

Solution I – pay the fee when buying the stock:

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;
}

Solution II – pay the fee when selling the stock:

public int maxProfit(int[] prices, int fee) {
long T_ik0 = 0, T_ik1 = Integer.MIN_VALUE; for (int price : prices) {
long T_ik0_old = T_ik0;
T_ik0 = Math.max(T_ik0, T_ik1 + price - fee);
T_ik1 = Math.max(T_ik1, T_ik0_old - price);
} return (int)T_ik0;
}

III — Summary

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(解决方案). The results are then applied to each of the six cases, with the last two using slightly modified(改进的) recurrence(再发生) relations due to the additional(附加的) requirements. I should mention that peterleetcode also introduced a nice solution here which generalizes(概括) to arbitrary(任意的) k values. If you have a taste, take a look.

Hope this helps and happy coding!

精帖转载(关于stock problem)的更多相关文章

  1. LeetCode 123. Best Time to Buy and Sell Stock III (stock problem)

    Say you have an array for which the ith element is the price of a given stock on day i. Design an al ...

  2. LeetCode 122. Best Time to Buy and Sell Stock II (stock problem)

    Say you have an array for which the ith element is the price of a given stock on day i. Design an al ...

  3. 二维剪板机下料问题(2-D Guillotine Cutting Stock Problem) 的混合整数规划精确求解——数学规划的计算智能特征

    二维剪板机下料问题(2-D Guillotine Cutting Stock Problem) 的混合整数规划精确求解——数学规划的计算智能特征 二维剪板机下料(2D-GCSP) 的混合整数规划是最优 ...

  4. LeetCode 309. Best Time to Buy and Sell Stock with Cooldown (stock problem)

    Say you have an array for which the ith element is the price of a given stock on day i. Design an al ...

  5. LeetCode 188. Best Time to Buy and Sell Stock IV (stock problem)

    Say you have an array for which the ith element is the price of a given stock on day i. Design an al ...

  6. 动态规划-Stock Problem

    2018-04-19 19:28:21 股票问题是leetcode里一条非常经典的题目,因为其具有一定的现实意义,所以还是在数学建模方面还是有很多用武之地的.这里会对stock的给出一个比较通用的解法 ...

  7. LeetCode 121. Best Time to Buy and Sell Stock (stock problem)

    Say you have an array for which the ith element is the price of a given stock on day i. If you were ...

  8. Apache2.4权限配置(原创帖-转载请注明出处)

    ==================说在前面的话================= 1:这次实验使用的php项目是Discuz,Discuz的安装请参照:http://www.cnblogs.com/ ...

  9. Cisco策略路由(policy route)精解(转载)

    原文:http://www.guanwei.org/post/Cisconetwork/07/Cisco-policy-route_8621.html 注:PBR以前是CISCO用来丢弃报文的一个主要 ...

随机推荐

  1. hdu 2121 Ice_cream’s world II (无定根最小树形图)

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=2121 题目大意: 有n个点,有m条单向路,问这n个点组成最小树形图的最小花费. 解题思路: 1:构造 ...

  2. 配置Ubuntu16.04第01步:U盘安装 Ubuntu 16.04系统

    Ubuntu 每年发布两个版本,Ubuntu 16.04 开发代号为“Xenial Xerus”,为第六个长期支持(LTS)版本. 1. 制作U盘系统安装盘 1.1下载最新的Universal USB ...

  3. 题解报告:hdu 1230 火星A+B(字符串)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1230 Problem Description 读入两个不超过25位的火星正整数A和B,计算A+B.需要 ...

  4. dalvik.system.VMRuntime 隐藏api的迷惑

    [Android UI界面]关于dalvik.system.VMRuntime 的 使用迷惑 我也遇到了相同问题.不知楼主现在解决了没有? 回答1: [Android UI界面]关于dalvik.sy ...

  5. js连续赋值,你理解了吗

    看一道有意思的题,也许你会自信满满地写下答案,会是正确的吗? }; var b = a; a.x = a = {n: }; console.log('a',a); console.log('b',b) ...

  6. poj2393 Yogurt factory

    思路: 贪心. 实现: #include <iostream> #include <cstdio> #include <algorithm> using names ...

  7. flex弹性布局操练1

    实现弹性布局之前常用浮动,相对定位和绝对定位等,但是现在好了,随着flex的兴起,方便了很多,而且也符合未来响应式布局的方向. 理论的东西可参考css3手册,这里专注实操. 一:单个元素 <di ...

  8. Android 7.0 因为file://引起的FileUriExposedException异常

    最近作者又碰到因为android 7.0 引起的兼容问题了. 在7.0以前的版本: //创建临时图片 File photoOutputFile = SDPath.getFile("temp. ...

  9. struts2 <allowed-methods > 标签配置

    1.在struts2   2.5版本中添加了对方法访问的权限,如果没有被添加到<allow-method> 方法的标签,将会报一下错误 5:05:18.078 [http-apr-8020 ...

  10. windows server 2008 r2 IIS7下网站配置 只允许指定的IP地址访问

    步骤一.找到ip地址和域限制 步骤二.添加全部拒绝 步骤三.添加允许访问的ip地址(局域网填写局域网ip,公网填写公网ip)  步骤四:如果想要拒绝某些ip访问,直接在规则中添加拒绝条目就可以