买卖股票的最佳时机--动态规划
本人已迁移博客至掘进,以后会在掘进平台更新最新的文章也会有更多的干货,欢迎大家关注!!!https://juejin.im/user/588993965333309
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。
注意你不能在买入股票前卖出股票。
示例 1:
输入: [7,1,5,3,6,4] 输出: 5 解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。 注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。
示例2:
输入: [7,6,4,3,1] 输出: 0 解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
解法一: 暴力解决法
算法思想:
我们需要找出给定数组中两个数字之间的最大差值(即,最大利润)。此外,第二个数字(卖出价格)必须大于第一个数字(买入价格)。
对于每组 i 和 j (j > i)我们需要找出max(prices[j] - prices[i])
代码如下:
func maxProfit(_ prices: [Int]) -> Int { guard prices.count > 0 else {return 0} var maxProfit: Int = 0 for i in 0..<prices.count - 1 { for j in i+1..<prices.count { maxProfit = max(maxProfit, prices[j] - prices[i]) } } return maxProfit }
解法二:动态规划
算法思想:
动态规划的3个步骤:
- 设定状态
- 推导方程
- 起始值和输出
这里状态的设置得稍微想一想,数组的问题一般我们得固定其中一个变量。这道题有两个变量,一个是“买入”,另一个是“卖出”,这里我们固定“卖出”变量,“买入”变量一定在它之前。
因此状态dp[i]表示:在索引为 i 的这一天,用户所能获得的最大利润。
下面思考状态转移,我们就要想dp [i] 的从前面的状态值转移过来,但是我们就会发现新的问题dp[i - 1]、dp[i - 2]、……、dp[0],状态转移无从下手。这里就说明:状态还不够,得把“买入”和“卖出”操作加入到状态的设置中。
于是下面修改状态的定义:
- dp[i][0]表示:在索引为 i 的这一天,用户手上不持股所能获得的最大利润;
1、 0 一般表示没有,在索引为 i 的这一天,手上没有股票,语义上也是清晰的,下面对状态 1 的理解也是一样; 2、 “用户手上不持股”不代表用户一定在索引为 i 的这一天把股票抛售了; 3、 在索引为 i 的这一天具体应该这样理解:从索引为 0 的天数开始,到索引为 i 的这一天,因此,这个状态的设置具有“前缀”的意味,因此输出是 dp[len - 1][0],不可能是 dp[len - 1][1],在只发生一次交易的情况下,持有这支股票一定不能使我们获得最大利润。
- 状态dp[i][1]表示:在索引为 i 的这一天,用户手上持股所能获得最大的利润。
下面考虑状态转移方程:
dp[i][0]
可以由谁转移过来?
dp[i - 1][0]
:今天不持股,假设我今天什么都不操作,当然可以从昨天不持股转移过来,这一点是显然的-
dp[i - 1][1] + prices[i]:昨天持股,就在索引为 i 的这一天,我卖出了股票,状态由 1 变成了 0,此时获得利润,因此加上这一天的股价。
综上:
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
2. dp[i][1]
可以由谁转移过来?
-
dp[i - 1][1]
:今天持股,假设我今天什么都不操作,当然可以从昨天持股转移过来,这一点是显然的; -
-prices[i]:**请注意:**状态 1 不能由状态 0 来,因为事实上,状态 0 特指:“卖出股票以后不持有股票的状态”,请注意这个状态和“没有进行过任何一次交易的不持有股票的状态”的区别。
因此,
-prices[i]
就表示,在索引为i
的这一天,执行买入操作得到的收益,再次强调:因为题目只允许一次交易,因此不能加上dp[i - 1][0]
。
综上:dp[i][1] = max(dp[i - 1][1], -prices[i]);
在新修改的定义中,我们已经分析出了输出dp[len - 1][0]
public int maxProfit(int[] prices) { int len = prices.length; if (len < 2) { return 0; } // 0:用户手上不持股所能获得的最大利润,特指卖出股票以后的不持股,非指没有进行过任何交易的不持股 // 1:用户手上持股所能获得的最大利润 // 注意:因为题目限制只能交易一次,因此状态只可能从 1 到 0,不可能从 0 到 1 // 状态转移方程: // dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i]) // dp[i][1] = max(dp[i - 1][1], -prices[i]) int[][] dp = new int[len][2]; dp[0][0] = 0; dp[0][1] = -prices[0]; for (int i = 1; i < len; 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[len - 1][0]; }
以上就是股票最大收益算法,希望对大家有所帮助!!!