前言
动态规划专题,从简到难通关动态规划。
每日一题
今天的题目是 122. 买卖股票的最佳时机 II
给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。
在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。
返回 你能获得的 最大 利润 。
示例 1:
输入: prices = [7,1,5,3,6,4] 输出: 7 解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4 。 随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6 - 3 = 3 。 总利润为 4 + 3 = 7 。
示例 2:
输入: prices = [1,2,3,4,5] 输出: 4 解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4 。 总利润为 4 。
示例 3:
输入: prices = [7,6,4,3,1] 输出: 0 解释: 在这种情况下, 交易无法获得正利润,所以不参与交易可以获得最大利润,最大利润为 0 。
提示:
1 <= prices.length <= 3 * 1040 <= prices[i] <= 104
题解
动态规划
- 定义dp数组以及下标含义
-
dp[i][0]表示第i天卖出股票所能获得的最大利润 -
dp[i][1]表示第i天买入股票所能获得的最大利润
- 推导递推公式
考虑第i天可以进行的两项操作:卖出股票或买入股票。如果要卖出股票,则在前i-1天中必须至少进行一次买入操作。因此,在第i天可以获得的最大利润分为两种情况:
- 未持有股票:如果在前i-1天中已经卖出了股票,那么第i天不进行任何操作;否则,如果在前i-1天中持有了股票,那么在第i天进行卖出操作,获得的利润是
dp[i-1][1]+prices[i]。 - 持有股票:如果在前i-1天中已经持有了股票,那么第i天不进行任何操作;否则,在第i天进行买入操作,获得的利润是
dp[i-1][0]-prices[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数组
定义一个二维数组dp
初始值需要考虑边界情况。在本问题中可以确定的是,如果只有1天,那么不进行任何交易,收益为0。因此,当数组prices长度为1时,我们直接返回0即可。
于其他情况,我们需要初始化dp数组。当第1天买进股票时,所需要的成本就是prices[0],因此我们可以初始化dp[0][1] = -prices[0]。当第1天卖出股票时,由于此时尚未拥有任何股票,因此利润也为0,我们可以初始化dp[0][0] = 0。根据dp数组的定义,在其他情况下,我们可以将二维数组dp所有元素初始化为0,代码实现如下:
let dp = Array.from(new Array(2), () => new Array(2).fill(0));
dp[0][0] = 0;
dp[0][1] = -prices[0];
- 确定遍历顺序
由递推公式可得,按照顺序遍历数组prices中的所有元素
代码
function maxProfit(prices: number[]): number {
let len = prices.length;
if (len <= 1) return 0;
let dp = Array.from(new Array(2), () => new Array(2).fill(0));
dp[0][1] = -prices[0];
dp[1][0] = 0;
dp[1][1] = Number.NEGATIVE_INFINITY;
for (let i = 1; i < len; 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) % 2][0] - prices[i]);
}
return dp[(len - 1) % 2][0];
};