大家好,我是河海哥,专注于后端,如果可以的话,想做一名code designer而不是普通的coder,一起见证河海哥的成长,您的评论与赞是我的最大动力。
1-1:题目
You are given an array prices where prices[i] is the price of a given stock on the ith day.
Find the maximum profit you can achieve. You may complete at most two transactions.
Note: You may not engage in multiple transactions simultaneously (i.e., you must sell the stock before you buy again).
Example 1:
Input: prices = [3,3,5,0,0,3,1,4]
Output: 6
Explanation: Buy on day 4 (price = 0) and sell on day 6 (price = 3), profit = 3-0 = 3.
Then buy on day 7 (price = 1) and sell on day 8 (price = 4), profit = 4-1 = 3.
Example 2:
Input: prices = [1,2,3,4,5]
Output: 4
Explanation: Buy on day 1 (price = 1) and sell on day 5 (price = 5), profit = 5-1 = 4.
Note that you cannot buy on day 1, buy on day 2 and sell them later, as you are engaging multiple transactions at the same time. You must sell before buying again.
Example 3:
Input: prices = [7,6,4,3,1]
Output: 0
Explanation: In this case, no transaction is done, i.e. max profit = 0.
Example 4:
Input: prices = [1]
Output: 0
Constraints:
1 <= prices.length <= 105
0 <= prices[i] <= 105
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii
1-2:idea
- 思路1:有问题2的答案,把持有股票作为一种状态的思想,我想着能不能再加一个维度去代表是第几次交易,这样就可以保证,第二次交易一定在第一次交易之后。然后我就按照这个思路写了一份代码,事实证明,还是比较复杂的,尤其在初始化的时候,没有想好,一只不能ac。
- 思路2:对于某一天的交易情况一共有5种
那么就可以用- 没有操作,不买入也不卖出 - 第一次买入 - 第一次卖出 - 第二次买入 - 第二次卖出
dp[i][j]
代表第i天,处于第j个状态时的收入。这边确实要注意,比如处于第二次买入的状态,并不一定说那一天必须是买入,也可能是之前发生过第二次买入,但是当天没有买入。比如[买,无,卖,无,无,无,买,无,无,i]那么i也是处于第二次买入的状态,因为之前已经出现过两次买入了,没有处于二次买的状态,那么i+1天就不能卖了。
1-3:idea1代码
public static int maxProfit(int[] prices) {
int n = prices.length;
// dp[i][j][k] 代表第i[1,n]天,是否持有股票[0,1],第k[0,2]次交易的最大收入,规定卖出的时候交易次数+1
int[][][] dp = new int[n + 1][2][3];
for (int k = 0; k <= 2; k++) {
dp[1][0][k] = 0;
dp[1][1][k] = -prices[0];
}
for (int i = 2; i < n + 1; i++) {
int price = prices[i - 1];
for (int j = 0; j <= 2; j++) {
// 第0次交易
if (j == 0) {
dp[i][0][j] = dp[i - 1][0][j];
} else {
// 今天不持有 = max(昨天不持有,昨天持有状态+今天卖出),昨天持有状态一定是上一次交易,所以这边j是要-1的
dp[i][0][j] = Math.max(dp[i - 1][0][j], dp[i - 1][1][j - 1] + price);
}
// 今天持有 = max(昨天持有,昨天不持有状态+今天买入),昨天不持有状态,今天持有,仍然是一个交易之下,j不用动。
dp[i][1][j] = Math.max(dp[i - 1][1][j], dp[i - 1][0][j] - price);
}
}
List<Integer> list = new ArrayList<>();
System.out.println(Arrays.deepToString(dp));
for (int i = 0; i <= 1; i++) {
for (int j = 0; j <= 2; j++) {
// dp[n][0][0], dp[n][1][0], dp[n][0][1], dp[n][1][1], dp[n][0][2], dp[n][1][2]
list.add(dp[n][i][j]);
}
}
return Collections.max(list);
}
有几个注意点:
- 这段代码是简化之后的,你可以先把j=0,1,2的时候dp状态转移方程依次列出来,之后再这样进行简化。我一开始是不能直接这样用j内循环写出来的。
- 最大值的位置:最后找最大值其实也有技巧,但是我没有想到,最后一次卖出的时候是最大值的位置。
- 初始化:初始化这边也有点困难,我一开始感觉当天买,当天卖不符合题意,题目说当天买不能当天卖,应该不存在这种情况,但是看完解答,当天买当天卖不会影响最后的结果,比如
dp[1][0][2] = 0
; 第一天不持有股票,并且是第二次卖出的情况,也就是买,卖,买,卖这几个都发生了在同一天。
1-4:idea2代码
public static int maxProfit2(int[] prices) {
int n = prices.length;
// dp[i][j]代表第i天,
// j代表第i天第状态,共五种。
// - 0:当天无交易
// - 1:第一次买入
// - 2:第一次卖出
// - 3:第二次买入
// - 4:第二次卖出
int[][] dp = new int[n + 1][5];
// initialization
for (int i = 0; i < dp[0].length; i++) {
if (i % 2 == 1) {
dp[1][i] = -prices[0];
}
}
for (int i = 2; i < n + 1; i++) {
int price = prices[i - 1];
dp[i][0] = dp[i - 1][0];
for (int j = 1; j <= 4; j++) {
// j is odd
if (j % 2 == 1) {
dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - 1] - price);
} else {
dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - 1] + price);
}
}
// // 第i天第一次买入 = max(当天无操作,当天买入),dp[i - 1][0]代表前一天处于无操作下的最大收入。不处于无操作状态,就不能第一次买入。
// dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - price);
// // 第i天第一次卖出 = max(当天无操作,当天卖出),dp[i - 1][1]代表前一天处于买入状态下的最大收入。不处于买入状态,就不能第一次买入。
// dp[i][2] = Math.max(dp[i - 1][2], dp[i - 1][1] + price);
// dp[i][3] = Math.max(dp[i - 1][3], dp[i - 1][2] - price);
// dp[i][4] = Math.max(dp[i - 1][4], dp[i - 1][3] + price);
}
System.out.println(Arrays.deepToString(dp));
return dp[n][4];
}
这里注释足够细致,我把所有的dp状态转移方程都写出来了。应该比较好理解了。初始化,也是之前的那个思路,我们可以用滚动数组的思想,给出第二版:
public static int maxProfit3(int[] prices) {
int n = prices.length;
// dp[i][j]代表第i天,
// j代表第i天第状态,共五种。
// - 0:当天无交易
// - 1:第一次买入
// - 2:第一次卖出
// - 3:第二次买入
// - 4:第二次卖出
int[][] dp = new int[n + 1][5];
int nothing = 0;
int buy1 = -prices[0];
int sell1 = 0;
int buy2 = -prices[0];
int sell2 = 0;
for (int i = 2; i < n + 1; i++) {
int price = prices[i - 1];
buy1 = Math.max(buy1, nothing - price);
sell1 = Math.max(sell1, buy1 + price);
buy2 = Math.max(buy2, sell1 - price);
sell2 = Math.max(sell2, buy2 + price);
}
return sell2;
}
1-5:总结
这道题有点难,我自己的想法靠自己写无法ac,根据别人的评论,自己修改才最后ac,答案的那种方法,我确实想不到,用一个维度去列举每一天的所有状态。就当是一种解题的积累吧!加油呀,河海哥,至少保证以后做过的题会写就行,没做过的不会也没办法。您的评论和点赞是我续写的动力。如有错误,请指正,个人水平很有限。