BM81 买卖股票的最好时机(二)
知识点贪心动态规划
描述
假设你有一个数组prices,长度为n,其中prices[i]是某只股票在第i天的价格,请根据这个价格数组,返回买卖股票能获得的最大收益1. 你可以多次买卖该只股票,但是再次购买前必须卖出之前的股票2. 如果不能获取收益,请返回03. 假设买入卖出均无手续费
数据范围: ,
要求:空间复杂度
,时间复杂度
进阶:空间复杂度 ,时间复杂度
示例1
输入:
[8,9,2,5,4,7,1]
复制返回值:
7
复制说明:
在第1天(股票价格=8)买入,第2天(股票价格=9)卖出,获利9-8=1
在第3天(股票价格=2)买入,第4天(股票价格=5)卖出,获利5-2=3
在第5天(股票价格=4)买入,第6天(股票价格=7)卖出,获利7-4=3
总获利1+3+3=7,返回7
示例2
输入:
[5,4,3,2,1]
复制返回值:
0
复制说明:
由于每天股票都在跌,因此不进行任何交易最优。最大收益为0。
示例3
输入:
[1,2,3,4,5]
复制返回值:
4
复制说明:
第一天买进,最后一天卖出最优。中间的当天买进当天卖出不影响最终结果。最大收益为4。
题解
贪心解法
对于一个数组的任意一个片段,一定存在这样的三个序列中的至少一个:
- 平滑序列:连续的元素值都相等
- 上升序列:a[i] > a[i-1]
- 下降序列:a[i]<a[i-1]
对于股票的买卖时机,实际上就是找出这个数组中所有的上升序列,并求这些序列差值之和。假如有一个长度为n的上升序列a,那么a[n-1] - a[0]就是我们要的利润差值。按照这个思路,我们需要找出题目给定数组中所有上升序列的起始位置和结束位置。但是如果我们将上面的公式变换一下,就不需要进行记录了:
profit = a[n-1] - a[0] = (a[n-1]-a[n-2]) + (a[n-2] - a[0]) = (a[n-1] - a[n-2]) + (a[n-2] -a[n-2])+...+(a[1]-a[0])
根据上面的等式可知,上升序列两段的差值,实际上等于上升序列中所有相邻2个元素差值之和。因此可以很容易的写出代码,如下:
using namespace std;
int maxProfit(vector<int> &prices)
{
if (prices.size() <= 1)
{
return 0;
}
int max_profit = 0;
for (int i = 1; i < prices.size(); ++i)
{
if (prices[i] > prices[i - 1])
{
max_profit += (prices[i] - prices[i - 1]);
}
}
return max_profit;
}
动态规划解法
int maxProfit_dp(vector<int> &prices)
{
if (prices.empty())
{
return 0;
}
// dp[i]表示当前的最大利润
std::vector<int> dp(prices.size(), 0);
for (int i = 1; i < prices.size(); ++i)
{
if (prices[i] < prices[i - 1])
{
dp[i] = dp[i - 1];
}
else
{
dp[i] = dp[i - 1] + prices[i] - prices[i - 1];
}
}
return dp.back();
}