假设把某股票的价格按照时间先后顺序存储在数组中,请问买卖 一次 该股票可能获得的利润是多少?
例如一只股票在某些时间节点的价格为 [9,11,8,5,7,12,16,14]。
如果我们能在价格为 5 的时候买入并在价格为 16 时卖出,则能收获最大的利润 11。
数据范围
输入数组长度 [0,500]。
样例
输入:[9, 11, 8, 5, 7, 12, 16, 14]输出:11
这道题咱们根据题意,题干说明在某一天买入并在某一天卖出,那肯定是买入的天数小于等于卖出 的天数,所以咱们这道题可以用三种方法,一种利用两层循环分别枚举买入与卖出的第几天比较最大值,一种是利用贪心找到买入的最小值来使利润最大,还有一种就是这道题是最值问题,肯定少不了动态规划,所以下面以这两种方法进行解析。
暴力法:
咱们先利用两层for循环分别枚举天数,因为卖出的天数肯定会大于买入的天数,所以咱们将卖出放在外层循环,内层循环为买入的天数。
class Solution {
public:int maxDiff(vector<int>& nums) {if(nums.empty()) return 0; //判断该vector是否为空int maxv = 0,imax = 0;for(int i = 1;i <= nums.size() - 1;i++){for(int j = 0;j < i;j++){maxv = max(maxv,nums[i] - nums[j]); //选出在第i天卖出的最大值}imax = max(maxv,imax); //通过比较前i天的利润,选出最大值}return imax; //并返回最大利润}
};
贪心法:
先枚举卖出的天数,用res比较出第i天卖出时的最大利润,并用贪心贪到前面买入的最小花费,以便于第i + 1天找到最大利润。
class Solution {
public:int maxDiff(vector<int>& nums) {if(nums.empty()) return 0;int res = 0;for(int i = 1,minv = nums[0];i <= nums.size() - 1;i++){res = max(res , nums[i] - minv);//第i天卖出的最大利润minv = min(minv , nums[i]); //minv存入的是前i天的最小买入的金钱}return res; //返回最大利润}
};
动态规划:
分析动态规划的时候先研究其dp数组的含义以及其数组内每一维数的含义。
如果咱们要是直接将dp[i]数组含义表示为前i天的最大利润,那么我们发现进行状态表示的时候很难判断当天的买入与卖出与不进行操作的状态,所以这里设置二维数组,dp[i][0]表示前i天买入的最大价值,dp[i][1]表是前i天卖出的最大价值。
设置完dp数组的含义,我们将其集合表示,分析其状态转移:
①对于dp[i][0]集合,我们可以将其用买入与不买入的两个集合表示:
买入的集合表示成-nums[i],因为买入我们是要花费钱,所以这里就要表示成负数;不买入的集合表示成dp[i - 1][0]。
dp[i][0] = max(dp[i - 1][0] , -nums[i]);
②而对于dp[i][1]集合,我们可以也是同样的道理,表示成卖出与不卖出这两个集合:
卖出的集合表示为dp[i - 1][0] + nums[i],不卖出的集合为dp[i - 1][1]。
dp[i][1] = max(dp[i - 1][1] , dp[i - 1][0] + nums[i]);
(这里的集合表示是通过dp数组的含义推出)
注意,这里咱们分析完状态转移之后,要分析其初始化问题,这里因为咱们的动态规划递推过程的开始是以dp[0][0]进行,所以这个dp[0][0]不能赋值为0,要根据其含义赋值,dp[0][0]代表的含义为买入第0个物品的最大价值,所以这里的dp[0][0] = -nums[0]。
下面看一下完整代码:
class Solution {
public:int maxDiff(vector<int>& nums) {if(nums.empty()) return 0;int dp[1000][3];dp[0][0] = -nums[0];for(int i = 1;i <= nums.size() - 1;i++){dp[i][0] = max(dp[i - 1][0] , -nums[i]);dp[i][1] = max(dp[i - 1][1] , dp[i - 1][0] + nums[i]);}return dp[nums.size() - 1][1];}
};
好了,本期内容就到这里了,下期继续讲解动态规划相关知识。
感谢收看,记得三连支持。