121. 买卖股票的最佳时机(买1次卖1次)
这题是基础变式,是可以用贪心解的,贪心代码如下:
class Solution {
public int maxProfit(int[] prices) {
int minPrice = Integer.MAX_VALUE;
int maxProfit = 0;
for (int i = 0; i < prices.length; i++) {
if (prices[i] < minPrice) {
minPrice = prices[i];
}
if (prices[i] - minPrice > maxProfit) {
maxProfit = prices[i] - minPrice;
}
}
return maxProfit;
}
}
- 找到最小的价格买进,然后再出现小价格之后找到最大价格卖出。
正统更广泛的解法是动态规划。
- dp数组含义
dp[i][0] 表示第i天持有股票所得最多现金
dp[i][1] 表示第i天不持有股票所得最多现金
- 递推公式
如果第i天持有股票即dp[i][0], 那么可以由两个状态推出来
- 第i-1天就持有股票,那么就保持现状,所得现金就是昨天持有股票的所得现金 即:dp[i - 1][0]
- 第i天买入股票,所得现金就是买入今天的股票后所得现金即:-prices[i]
dp[i][0] = max(dp[i - 1][0], -prices[i])
如果第i天不持有股票即dp[i][1], 也可以由两个状态推出来
- 第i-1天就不持有股票,那么就保持现状,所得现金就是昨天不持有股票的所得现金 即:dp[i - 1][1]
- 第i天卖出股票,所得现金就是按照今天股票价格卖出后所得现金即:prices[i] + dp[i - 1][0]
同样dp[i][1]取最大的,dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0]);
其中dp[n - 1][1]就是最终结果!
因为本题中不持有股票状态所得金钱一定比持有股票状态得到的多!
- dp数组如何初始化
dp[0][0] -= prices[0];
dp[0][1]表示第0天不持有股票,不持有股票那么现金就是0,所以dp[0][1] = 0;
- 遍历顺序
没啥好说的,从前到后。
- 推导dp数组
最终代码(二维数组版)
class Solution {
public int maxProfit(int[] prices) {
int dp[][] = new int[prices.length][2];
dp[0][0] = -prices[0];
dp[0][1] = 0;
for(int i = 1; i < prices.length; i++){
dp[i][0] = Math.max(dp[i - 1][0], -prices[i]);
dp[i][1] = Math.max(dp[i - 1][1], prices[i] + dp[i - 1][0]);
}
return dp[prices.length - 1][1];
}
}
这下知道买入股票为什么是-price[i]了!这样prices[i] + dp[i - 1][0]才能表示利润
122.买卖股票的最佳时机II(无数次买无数次卖)
本题也可以用贪心算法,代码如下:
class Solution {
public int maxProfit(int[] prices) {
int res = 0;
for(int i = 0; i < prices.length; i++){
if(i > 0 && prices[i] - prices[i - 1] > 0){
res += prices[i] - prices[i - 1];
}
}
return res;
}
}
只要当天价格比前一天价格高,就卖出,最后差值直接相加。
动态规划
这题跟上一题的差距只在递推公式上。原来只能买卖一次的是:
dp[i][0] = max(dp[i - 1][0], -prices[i])//第i天持有股票
dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0]);//第i天不持有股票
本题的递推公式是:
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]);
dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + prices[i]);
可以看出区别在于原来如果买进,dp[i][0]是-prices[i],现在变成了dp[i - 1][1] +(- prices[i]),其他都一样。因为一只股票可以买卖多次,所以当第i天买入股票的时候,所持有的现金可能有之前买卖过的利润。
最终代码:
class Solution {
public int maxProfit(int[] prices) {
int dp[][] = new int[prices.length][2];
dp[0][0] = -prices[0];
dp[0][1] = 0;
for(int i = 1; i < prices.length; 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] + dp[i - 1][0]);
}
return dp[prices.length - 1][1];
}
}