题目描述
给定一个数组 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 。
示例 2:
输入: prices = [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
示例 3:
输入: prices = [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
题解
解题思路1 - 计算所有正增长
由于股票的购买没有限制,那么把能赚钱的交易都累加上就是最大利润了
每天的盈利都获得,每天的亏损都规避。
所以最大收益就是每天比前一天的正差价总和
+ (int)maxProfit1:(NSArray *)prices {
int ans = 0;
for (int i=1; i[prices[i-1] intValue]) {
ans += [prices[i] intValue] - [prices[i-1] intValue];
}
}
return ans;
}
static public func maxProfit1(_ prices: [Int]) -> Int {
var ans = 0;
for i in 1.. prices[i-1] {
ans += prices[i] - prices[i-1]
}
}
return ans;
}
解题思路 2 - 贪心算法
由于股票的购买没有限制,因此整个问题等价于寻找x个不相交的区间(li,ri],使得x个区间的a[ri]-a[li]的和值最大。
x
∑ a[ri] - a[li]
i=1
其中li表示第li天买入的值,ri表示第ri天卖出
其中区间(li,ri]贡献的价值为a[ri]-a[li],其实等价于(li,li+1],(li+1,li+2],...(ri-1,ri]这若干个区间长度为1的区间的价值和,即a[ri]-a[li] = (a[ri]-a[ri-1]) + (a[ri-1]-a[ri-2]) + ... + (a[li+1]-a[li]),因此问题可以简化为找x个长度为1的区间(li,li+1],使得x个区间的a[li+1]-a[li]的和值最大
x
∑ a[li+1] - a[li] 值最大
I=1
贪心的角度考虑我们每次选择贡献大于0的区间即能使得答案最大化,因此最后答案为
n-1
∑ max{0,a[i] - a[i-1]}
i=1
+ (int)maxProfit2:(NSArray *)prices {
int ans = 0;
for (int i=1; i
static public func maxProfit2(_ prices: [Int]) -> Int {
var ans = 0;
for i in 1..
解题思路 3 - 动态规划
考虑到「不能同时参与多笔交易」,因此每天交易结束后只可能存在手里有一支股票或者没有股票的状态
定义状态dp[i][0]表示第i天交易后手里没有股票的最大利润,dp[i][1]表示第i天交易完后手里持有一只股票的最大利润,i从0开始
dp[i][0]的转移方程,这一天交易完后手里没有股票,那么可能的转移状态为前一天已经没有股票,即dp[i-1][0],或者前一天结束时持有股票,即dp[i-1][1],这时候我们要将其卖出并获到当天的收益prices[i],即dp[i-1][1]+prices[i],因此为了收益最大化,我们列出如下的转移方程:
dp[i][0] = max{dp[i-1][0],dp[i-1][1]+prices[I]}
dp[i][1]的转移方程,这一天交易完后手里还有股票,那么可能的转移状态为前一天手里就有股票,即dp[i-1][1],或者前一天结束时没有股票,即dp[i-1][0],这时候我们需要买进股票并减少收益prices[i],即dp[i-1][0]-prices[i],因此为了收益最大化,我们列出如下的转移方程:
dp[i][1] = max{dp[i-1][1],dp[i-1][0]-prices[I]}
对于初始状态,根据状态定义我们可以知道第0天交易结束的时候dp[0][0] = 0,dp[0][1] = -prices[0]
因此,我们只要从前往后依次计算状态即可。由于全部交易结束后,持有股票的收益一定低于不持有股票的收益,因而dp[n-1][0] 一定大于dp[n-1][1]的,因而最后的答案是dp[n-1][0]
+ (int)maxProfit3:(NSArray *)prices {
int n = (int)prices.count;
int dp[n][2];
dp[0][0] = 0;
dp[0][1] = -[prices[0] intValue];
for (int i=1; i
static public func maxProfit3(_ prices: [Int]) -> Int {
let n = prices.count
var dp = [[Int]](repeating: [Int](repeating: 0, count: 2), count: n)
dp[0][0] = 0
dp[0][1] = -prices[0]
for i in 1..
优化上述动态规划
注意到上面的状态转移方程中,每一天的状态只与前一天的状态有关,而与更早的状态都无关,因此我们不必存储这些无关的状态,只需要将dp[i−1][0] 和dp[i−1][1] 存放在两个变量中,通过它们计算出dp[i][0] 和dp[i][1] 并存回对应的变量,以便于第i+1 天的状态转移即可。
+ (int)maxProfit4:(NSArray *)prices {
int n = (int)prices.count;
int dp0 = 0;
int dp1 = -[prices[0] intValue];
for (int i=1; i
static public func maxProfit4(_ prices: [Int]) -> Int {
let n = prices.count
var dp0:Int = 0
var dp1:Int = -prices[0]
for i in 1..
参考 https://leetcode-cn.com/leetbook/read/top-interview-questions-easy/x2zsx1/