原题见: https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/java
给定一个股票价格(按日期排序)的列表,在最多能够买卖两次的状况下,如何最大化收益;算法
好比:1, 2, 1, 3, 0, 4;code
最大收益为6,在第三天买入,第四天卖出,第五天买入,第六天卖出;排序
先用动态规划的算法;用f(i, j) 表示在第i (from 0) 天到第j天,只买卖一次时的最大收益;那么,要计算买卖两次的收益,只须要找到一天k,使得f(0, k) + f(k + 1, n - 1)最大便可;leetcode
f(i, j) 是如下各式中的最大值:it
如下是主要的实现代码:io
func maxProfit(prices []int) int { prices = compact1(prices) prices = compact2(prices) if len(prices) == 0 { return 0 } n := len(prices) fx := make([][]int, n) for i := range fx { fx[i] = make([]int, n) } for k := 1; k < n; k++ { for i := 0; i+k < n; i++ { j := i + k fx[i][j] = max(0, prices[j]-prices[i]) if k == 1 { continue } fx[i][j] = max(fx[i][j], fx[i+1][j]) fx[i][j] = max(fx[i][j], fx[i][j-1]) } } r := fx[0][n-1] for k := 1; k < n-1; k++ { if fx[0][k]+fx[k][n-1] > r { r = fx[0][k] + fx[k][n-1] } } return r }
由于数据可能会不少,在处理以前,先对数据进行了压缩,只保留那些会影响收益的数据;好比1, 2, 3, 4,只须要保留1, 4; 4, 3, 2, 1 只须要保留4, 1;class
这个算法的时间复杂度是o(n * n), 空间也须要o(n * n);im
但这个题目还有更精简更快速也更优美的算法,使人印象深入;具体的讨论能够参考 https://discuss.leetcode.com/topic/32288/2ms-java-dp-solution;数据
使用如下四个值表示当前的状态:
firstBuy: 第一次买入股票的收益;这个值表示为价格的负数,因此价格越低,收益越高;
firstSell: 第一次卖出股票的收益;firstSell = max(firstSell, firstBuy + currentPrice); 卖出时的价格(加上)第一次买入时的收益(负值);
secondBuy: 第二次买入股票时的收益;secondBuy = max(secondBuy, firstSell - currentPrice);
secondSell: 第二次卖出时的收益;secondSell = max(secondSell, secondBuy + currentPrice);
代码实现:
func maxProfit2(prices []int) int { firstBuy, firstSell := math.MinInt32, 0 secondBuy, secondSell := math.MinInt32, 0 for _, price := range prices { if firstBuy < -price { firstBuy = -price } if firstSell < firstBuy+price { firstSell = firstBuy + price } if secondBuy < firstSell-price { secondBuy = firstSell - price } if secondSell < secondBuy+price { secondSell = secondBuy + price } } return secondSell }