赞
踩
力扣上有若干道买股票求最大收益的问题,而这些问题都是可以由动态规划解决的,而且本质上的思想也是基本一致,这里将6道题目进行总结。
Say you have an array for which the ith element is the price of a given stock on day i.
If you were only permitted to complete at most one transaction (i.e., buy one and sell one share of the stock), design an algorithm to find the maximum profit.
Note that you cannot sell a stock before you buy one.
Example 1:
Input: [7,1,5,3,6,4]
Output: 5
Explanation: Buy on day 2 (price = 1) and sell on day 5 (price = 6), profit = 6-1 = 5.
Not 7-1 = 6, as selling price needs to be larger than buying price.
Example 2:
Input: [7,6,4,3,1]
Output: 0
Explanation: In this case, no transaction is done, i.e. max profit = 0.
dp[i][0]
:第 i
天结束时没有持有股票的最大收益;dp[i][1]
:第 i
天结束时持有股票的最大收益;dp[0][0] = 0
,第一天结束时没有股票则收益一定为零;dp[0][1] = -prices[0]
,第一天结束时持有股票则买入股票需支付 prices[0]
的价格;dp[i][0]
= max ( dp[i - 1][0]
, dp[i - 1][1]
+ prices[i]
) :第 i
天结束没有股票;可能是因为昨天结束就没有股票,还有可能今天结束将股票卖出(而这种情况要求昨天结束时一定有股票)。dp[i][1]
= max ( dp[i - 1][1]
, -prices[i]
) :第 i
天结束有股票,可能是因为昨天结束就有股票;还有可能今天刚买了股票,而由于股票交易只允许发生一次,所以今天买了以前一定没有买过,故状态与之前无关,即为 -prices[i]
。dp[n - 1][0]
,即最后一天没有股票时的最大收益。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]; dp[0][0] = 0; // base case dp[0][1] = -prices[0]; // base case 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], -prices[i]); } return dp[n - 1][0]; } }
由于在状态转移时,当前状态只和上一状态有关,故可以进行空间优化:
class Solution {
public int maxProfit(int[] prices) {
if (prices == null || prices.length == 0) {
return 0;
}
int n = prices.length;
int max_nostock = 0; // base case
int max_withstock = -prices[0]; // base case
for (int i = 1; i < n; i++) {
max_nostock = Math.max(max_nostock, max_withstock + prices[i]);
max_withstock = Math.max(max_withstock, -prices[i]);
}
return max_nostock;
}
}
思路其实很简单,即:如果在第 i
天卖出股票,那么为了让收益最大,那么一定要在 i
之前股票最便宜的一天买入股票,故可实现代码如下:
class Solution { public int maxProfit(int[] prices) { if (prices == null || prices.length < 2) { return 0; } int min = prices[0]; // min price before ith day int max = prices[1] - prices[0]; // max profit for (int i = 2; i < prices.length; i++) { if (prices[i - 1] < min) { min = prices[i - 1]; } max = Math.max(prices[i] - min, max); } if (max < 0) { return 0; } return max; } }
Say you have an array prices
for which the ith element is the price of a given stock on day i.
Design an algorithm to find the maximum profit. You may complete as many transactions as you like (i.e., buy one and sell one share of the stock multiple times).
Note: You may not engage in multiple transactions at the same time (i.e., you must sell the stock before you buy again).
Example 1:
Input: [7,1,5,3,6,4]
Output: 7
Explanation: Buy on day 2 (price = 1) and sell on day 3 (price = 5), profit = 5-1 = 4.
Then buy on day 4 (price = 3) and sell on day 5 (price = 6), profit = 6-3 = 3.
Example 2:
Input: [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: [7,6,4,3,1]
Output: 0
Explanation: In this case, no transaction is done, i.e. max profit = 0.
Constraints:
1 <= prices.length <= 3 * 10 ^ 4
0 <= prices[i] <= 10 ^ 4
dp[i][0]
:第 i
天结束时没有持有股票的最大收益;dp[i][1]
:第 i
天结束时持有股票的最大收益;dp[0][0] = 0
,第一天结束时没有股票则收益一定为零;dp[0][1] = -prices[0]
,第一天结束时持有股票则买入股票需支付 prices[0]
的价格;dp[i][0]
= max ( dp[i - 1][0]
, dp[i - 1][1]
+ prices[i]
) :第 i
天结束没有股票;可能是因为昨天结束就没有股票,还有可能今天结束将股票卖出(而这种情况要求昨天结束时一定有股票)。dp[i][1]
= max ( dp[i - 1][1]
, dp[i - 1][0]
- prices[i]
) :第 i
天结束有股票,可能是因为昨天结束就有股票;还有可能今天刚买了股票,和上题的区别是股票交易可以发生若干次。dp[n - 1][0]
,即最后一天没有股票时的最大收益。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]; dp[0][0] = 0; // base case dp[0][1] = -prices[0]; // base case 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][0] - prices[i]); } return dp[n - 1][0]; } }
状态转移时只和上一状态有关,故可以空间优化:
class Solution { public int maxProfit(int[] prices) { if (prices == null || prices.length == 0) { return 0; } int n = prices.length; int max_nostock = 0; // base case int max_withstock = -prices[0]; // base case for (int i = 1; i < n; i++) { int tmp = max_nostock; max_nostock = Math.max(max_nostock, max_withstock + prices[i]); max_withstock = Math.max(max_withstock, tmp - prices[i]); } return max_nostock; } }
分析部分转载自liweiwei1419大佬的题解。
贪心算法的直觉:由于不限制交易次数,只要今天股价比昨天高,就交易。
下面对这个算法进行几点说明:
该算法仅可以用于计算,但计算的过程并不是真正交易的过程,但可以用贪心算法计算题目要求的最大利润。下面说明等价性:以 [1, 2, 3, 4] 为例,这 4 天的股价依次上升,按照贪心算法,得到的最大利润是:
res = (prices[3] - prices[2]) + (prices[2] - prices[1]) + (prices[1] - prices[0])
= prices[3] - prices[0]
仔细观察上面的式子,按照贪心算法,在下标为 1、2、3 的这三天,做的操作应该是买进昨天的,卖出今天的,虽然这种操作题目并不允许,但是它等价于:在下标为 0 的那一天买入,在下标为 3 的那一天卖出。
这道题 「贪心」 的地方在于,对于 「今天的股价 - 昨天的股价」,得到的结果有 3 种可能:① 正数,② 0,③负数。贪心算法的决策是: 只加正数 。
class Solution {
public int maxProfit(int[] prices) {
if (prices == null || prices.length < 2) {
return 0;
}
int n = prices.length;
int max = 0;
for (int i = 1; i < n; i++) {
if (prices[i] > prices[i - 1]) {
max += prices[i] - prices[i - 1];
}
}
return max;
}
}
Say you have an array for which the ith element is the price of a given stock on day i.
Design an algorithm to find the maximum profit. You may complete at most two transactions.
Note: You may not engage in multiple transactions at the same time (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 <= 10^5
0 <= prices[i] <= 10^5
DP:
将买入股票定义为进行一次交易
dp[i][j][0]
:第 i
天结束时没有持有股票且经过了 j
次交易的最大收益;dp[i][j][1]
:第 i
天结束时持有股票且经过了 j
次交易的最大收益;dp[0][1][0] = Integer.MIN_VALUE / 2
,第一天结束时没有股票且经过了一次交易,这是不可能的,故设置为一个小值,而为了防止整数溢出,故选择 Integer.MIN_VALUE / 2
;dp[0][1][1] = -prices[0]
,第一天结束时持有股票且经过了1次交易则买入股票需支付 prices[0]
的价格;dp[0][2][0] = Integer.MIN_VALUE / 2
;d[0][2][1] = Ingeger.MIN_VALUE / 2
。dp[i][2][0]
= max ( dp[i - 1][2][0]
, dp[i - 1][2][1]
+ prices[i]
) :第 i
天结束经过了2次交易且没有股票,可能是因为昨天结束就经过了2次交易且没有股票;还有可能今天结束将股票卖出(而这种情况要求昨天结束时一定有股票)。dp[i][2][1]
= max ( dp[i - 1][2][1]
, dp[i - 1][1][0]
- prices[i]
) :第 i
天结束时经过了2次交易且有股票,可能是因为昨天结束就有股票;还有可能今天刚买了股票(即今天才发生第二次交易),则昨天的状态应为发生过1次交易且没有股票。dp[i][1][0]
= max ( dp[i - 1][1][0]
, dp[i - 1][1][1]
+ prices[i]
) :第 i
天结束经过了1次交易且没有股票,可能是因为昨天结束就经过了1次交易且没有股票;还有可能今天结束将股票卖出(而这种情况要求昨天结束时一定有股票)。dp[i][1][1]
= max ( dp[i - 1][1][1]
, -prices[i]
) :第 i
天结束时经过了1次交易且有股票,可能是因为昨天结束就有股票;还有可能今天刚买了股票(即今天才发生第一次交易)。dp[n - 1][1][0]
, dp[n - 1][2][0]
) ,即最后一天没有股票时的最大收益(可能经历了1次交易,可能经历了2次交易)。最后的
return
为什么要取经历1次交易和2次交易的更大者呢?考虑 [1, 2, 3, 4, 5] 这种情况,显然经历了2次交易的收益为
profit_2 = profit_trade1 + profit_trade2 = (2 - 1) + (4 - 3) = 2
- 1
而经历了1次交易的收益为
profit_1 = profit_trade = 5 - 1 = 4
- 1
所以不一定经过2次交易的会赚的多,所以需要二者比较取较大。
class Solution { public int maxProfit(int[] prices) { if (prices == null || prices.length == 0) { return 0; } int n = prices.length; int[][][] dp = new int[n][3][2]; dp[0][1][0] = Integer.MIN_VALUE / 2; dp[0][1][1] = -prices[0]; dp[0][2][0] = Integer.MIN_VALUE / 2; dp[0][2][1] = Integer.MIN_VALUE / 2; for (int i = 1; i < n; i++) { dp[i][2][0] = Math.max(dp[i - 1][2][0], dp[i - 1][2][1] + prices[i]); dp[i][2][1] = Math.max(dp[i - 1][2][1], dp[i - 1][1][0] - prices[i]); dp[i][1][0] = Math.max(dp[i - 1][1][0], dp[i - 1][1][1] + prices[i]); dp[i][1][1] = Math.max(dp[i - 1][1][1], -prices[i]); } int max = Math.max(dp[n - 1][2][0], dp[n - 1][1][0]); if (max < 0) { return 0; } return max; } }
进行空间优化后的代码如下:
class Solution { public int maxProfit(int[] prices) { if (prices == null || prices.length == 0) { return 0; } int n = prices.length; int dp_i10 = Integer.MIN_VALUE / 2; int dp_i11 = -prices[0]; int dp_i20 = Integer.MIN_VALUE / 2; int dp_i21 = Integer.MIN_VALUE / 2; for (int i = 1; i < n; i++) { int a = Math.max(dp_i20, dp_i21 + prices[i]); int b = Math.max(dp_i21, dp_i10 - prices[i]); int c = Math.max(dp_i10, dp_i11 + prices[i]); int d = Math.max(dp_i11, -prices[i]); dp_i20 = a; dp_i21 = b; dp_i10 = c; dp_i11 = d; } int max = Math.max(dp_i20, dp_i10); if (max < 0) { return 0; } return max; } }
You are given an integer array prices
where prices[i]
is the price of a given stock on the ith
day.
Design an algorithm to find the maximum profit. You may complete at most k
transactions.
Notice that you may not engage in multiple transactions simultaneously (i.e., you must sell the stock before you buy again).
Example 1:
Input: k = 2, prices = [2,4,1]
Output: 2
Explanation: Buy on day 1 (price = 2) and sell on day 2 (price = 4), profit = 4-2 = 2.
Example 2:
Input: k = 2, prices = [3,2,6,5,0,3]
Output: 7
Explanation: Buy on day 2 (price = 2) and sell on day 3 (price = 6), profit = 6-2 = 4. Then buy on day 5 (price = 0) and sell on day 6 (price = 3), profit = 3-0 = 3.
Constraints:
0 <= k <= 10^9
0 <= prices.length <= 1000
0 <= prices[i] <= 1000
将买入股票定义为进行一次交易
dp[i][j][0]
:第 i
天结束时没有持有股票且经过了 j
次交易的最大收益;dp[i][j][1]
:第 i
天结束时持有股票且经过了 j
次交易的最大收益;dp[0][1][0] = Integer.MIN_VALUE / 2
,第一天结束时没有股票且经过了一次交易,这是不可能的,故设置为一个小值,而为了防止整数溢出,故选择 Integer.MIN_VALUE / 2
;dp[0][1][1] = -prices[0]
,第一天结束时持有股票且经过了1次交易则买入股票需支付 prices[0]
的价格;dp[0][j][0] = Integer.MIN_VALUE / 2
,when j ≥ 2
;d[0][j][1] = Ingeger.MIN_VALUE / 2
,when j ≥ 2
。dp[i][j][0]
= max ( dp[i - 1][j][0]
, dp[i - 1][j][1]
+ prices[i]
) :第 i
天结束经过了 j
次交易且没有股票,可能是因为昨天结束就经过了 j
次交易且没有股票;还有可能今天结束将股票卖出(而这种情况要求昨天结束时一定有股票)。dp[i][j][1]
= max ( dp[i - 1][j][1]
, dp[i - 1][j - 1][0]
- prices[i]
) :第 i
天结束时经过了 j
次交易且有股票,可能是因为昨天结束就有股票;还有可能今天又买了股票(即昨天结束发生了 j - 1
次交易且没有股票)则今天发生了第 j
次交易。dp[n - 1][j][0]
) ,即最后一天没有股票时的最大收益(可能经历了1次交易,也可能经历了多次交易)。本题
k
的取值范围为 [0, 10^9],也就是说如果将dp[][][]
数组真的按照第二维度为k
定义的话,那必然会造成内存溢出的,所以应该考虑到,对于长度为n
的prices
数组来说,最多可以进行多少次交易呢?答案显而易见,是 ⌊n / 2⌋。也就是说当
k
大于该值时,本地就变成了123. Best Time to Buy and Sell Stock III题,即交易次数不限。
class Solution { public int maxProfit(int k, int[] prices) { if (prices == null || prices.length == 0 || k == 0) { return 0; } int n = prices.length; if (k > n / 2) { return maxProfitKNoLimit(prices); } int[][][] dp = new int[n][k + 1][2]; // base case dp[0][1][1] = -prices[0]; dp[0][1][0] = Integer.MIN_VALUE / 2; for (int i = 2; i <= k; i++) { dp[0][i][1] = Integer.MIN_VALUE / 2; dp[0][i][0] = Integer.MIN_VALUE / 2; } for (int i = 1; i < n; i++) { for (int j = 1; j <= k; j++) { 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]); } } int max = 0; for (int i = 1; i <= k; i++) { max = Math.max(max, dp[n - 1][i][0]); } if (max < 0) { return 0; } return max; } private int maxProfitKNoLimit(int[] prices) { if (prices == null || prices.length == 0) { return 0; } int n = prices.length; int max_nostock = 0; int max_withstock = -prices[0]; for (int i = 1; i < n; i++) { int tmp = max_nostock; max_nostock = Math.max(max_nostock, max_withstock + prices[i]); max_withstock = Math.max(max_withstock, tmp - prices[i]); } return max_nostock; } }
将第一维的天数优化掉,可以得到下面的空间优化代码:
class Solution { public int maxProfit(int k, int[] prices) { if (prices == null || prices.length == 0 || k == 0) { return 0; } int n = prices.length; if (k > n / 2) { return maxProfitKNoLimit(prices); } int[][] dp = new int[k + 1][2]; dp[1][1] = -prices[0]; dp[1][0] = Integer.MIN_VALUE / 2; for (int i = 2; i <= k; i++) { dp[i][1] = Integer.MIN_VALUE / 2; dp[i][0] = Integer.MIN_VALUE / 2; } for (int i = 1; i < n; i++) { for (int j = 1; j <= k; j++) { int a = Math.max(dp[j][0], dp[j][1] + prices[i]); int b = Math.max(dp[j][1], dp[j - 1][0] - prices[i]); dp[j][0] = a; dp[j][1] = b; } } int max = 0; for (int i = 1; i <= k; i++) { max = Math.max(max, dp[i][0]); } if (max < 0) { return 0; } return max; } }
Say you have an array for which the ith element is the price of a given stock on day i.
Design an algorithm to find the maximum profit. You may complete as many transactions as you like (ie, buy one and sell one share of the stock multiple times) with the following restrictions:
Example:
Input: [1,2,3,0,2]
Output: 3
Explanation: transactions = [buy, sell, cooldown, buy, sell]
DP:
考虑加入冻结后当天结束时的状态组合有:①有股票且冻结;②没股票且冻结;③没股票且未冻结。(有股票时冻结与否第二天都不能够买入股票,故不需要再分两种情况讨论)。
dp[i][0]
:第 i
天结束时持有股票的最大收益;dp[i][1]
:第 i
天结束时没有持有股票且冻结的最大收益;dp[i][2]
:第 i
天结束时没有持有股票且未冻结的最大收益。dp[0][0] = -prices[0]
,第一天结束时持有股票则买入股票需支付 prices[0]
的价格;dp[0][1] = dp[0][2] = 0
,第一天结束时没有股票收益默认为 0
;dp[i][0]
= max ( dp[i - 1][0]
, dp[i - 1][2]
- prices[i]
) :第 i
天结束有股票;可能是因为昨天结束就没有股票;还有可能今天买入了股票,而今天可以买入股票则昨天一定处于冻结状态。dp[i][1]
= dp[i][0]
+ prices[i]
:第 i
天结束没有股票且冻结,之所以冻结一定是今天卖了股票,则昨天一定是有股票状态。dp[i][2]
= max ( dp[i - 1][1]
, dp[i - 1][2]
) :第 i
天结束没有股票且未冻结,则昨天一定没有股票,但是可以是昨天本来就为冻结,或者是昨天冻结而今天解冻。dp[n - 1][1]
, dp[n - 1][2]
),即最后一天没股票时(可以是冻结状态)的最大收益。class Solution { public int maxProfit(int[] prices) { if (prices == null || prices.length == 0) { return 0; } int n = prices.length; int[][] dp = new int[n][3]; dp[0][0] = -prices[0]; for (int i = 1; i < n; i++) { dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][2] - prices[i]); dp[i][1] = dp[i - 1][0] + prices[i]; dp[i][2] = Math.max(dp[i - 1][1], dp[i - 1][2]); } return Math.max(dp[n - 1][1], dp[n - 1][2]); } }
将第一维天数优化掉后有如下空间复杂度为 O(1)
的代码:
class Solution { public int maxProfit(int[] prices) { if (prices == null || prices.length == 0) { return 0; } int n = prices.length; int max_withstock = -prices[0]; int max_nostockCooldown = 0; int max_nostockNoCooldown = 0; for (int i = 1; i < n; i++) { int a = Math.max(max_withstock, max_nostockNoCooldown - prices[i]); int b = max_withstock + prices[i]; int c = Math.max(max_nostockCooldown, max_nostockNoCooldown); max_withstock = a; max_nostockCooldown = b; max_nostockNoCooldown = c; } return Math.max(max_nostockCooldown, max_nostockNoCooldown); } }
Your are given an array of integers prices
, for which the i
-th element is the price of a given stock on day i
; and a non-negative integer fee
representing a transaction fee.
You may complete as many transactions as you like, but you need to pay the transaction fee for each transaction. You may not buy more than 1 share of a stock at a time (ie. you must sell the stock share before you buy again.)
Return the maximum profit you can make.
Example 1:
Input: prices = [1, 3, 2, 8, 4, 9], fee = 2
Output: 8
Explanation: The maximum profit can be achieved by:
Buying at prices[0] = 1Selling at prices[3] = 8Buying at prices[4] = 4Selling at prices[5] = 9The total profit is ((8 - 1) - 2) + ((9 - 4) - 2) = 8.
Note:
0 < prices.length <= 50000
.
0 < prices[i] < 50000
.
0 <= fee < 50000
.
DP:
这道题和就没有任何区别,就是购买股票时多花了手续费的钱,或者理解为卖出股票时少赚了手续费的钱。
dp[i][0]
:第 i
天结束时没有持有股票的最大收益;dp[i][1]
:第 i
天结束时持有股票的最大收益;dp[0][0] = 0
,第一天结束时没有股票则收益一定为零;dp[0][1] = -prices[0] - fee
,第一天结束时持有股票则买入股票需支付 prices[0]
的价格即手续费;dp[i][0]
= max ( dp[i - 1][0]
, dp[i - 1][1]
+ prices[i]
) :第 i
天结束没有股票;可能是因为昨天结束就没有股票,还有可能今天结束将股票卖出(而这种情况要求昨天结束时一定有股票)。dp[i][1]
= max ( dp[i - 1][1]
, dp[i - 1][0]
- prices[i]
- fee
) :第 i
天结束有股票,可能是因为昨天结束就有股票;还有可能今天刚买了股票,同时要多花手续费。dp[n - 1][0]
,即最后一天没有股票时的最大收益。直接上优化了空间的代码吧。。。
class Solution { public int maxProfit(int[] prices, int fee) { if (prices == null || prices.length == 0) { return 0; } int n = prices.length; int max_nostock = 0; int max_withstock = -prices[0] - fee; for (int i = 1; i < n; i++) { int tmp = max_nostock; max_nostock = Math.max(max_nostock, max_withstock + prices[i]); max_withstock = Math.max(max_withstock, tmp - prices[i] - fee); } return max_nostock; } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。