一、买卖股票的最佳时机
虽然这题标记的是简单,个人感觉还是挺难的。。。也许是我太菜了吧,找了题解才会做,看了大佬的题解后,发现这是一种题型,真是醍醐灌顶。觉得应该总结一下,作为自己的刷题笔记。下面进入正文。
老司机一眼可以看出,这种求差问题可以转换成区间和的问题(emmmmm,我肯定看不出来)。为什么呢?因为我们有牛顿莱布尼茨公式:
在上面的公式中,表示的数组称为前缀和,其实也就是题目所给的当天股票价格,最大的区间和可用动态规划求解,公式如下:
其中,表示在不亏钱的前提下,第
天卖出股票所赚的钱,若
等于0了,则说明第
天卖股票亏了,将
重新置0。
数组表示相邻两天股票价格的差
int maxProfit(vector<int>& prices) {
if(prices.size() < 2) return 0;//股票只有一天,不赚钱
vector<int> diff;
for(int i=0; i<prices.size()-1; i++){
diff.push_back(prices[i+1] - prices[i]);
}
vector<int> dp(diff.size());
dp[0] = max(0, diff[0]);
int profit = dp[0];
for(int i=1; i<dp.size(); i++){
dp[i] = max(0, dp[i-1] + diff[i]);
profit = max(profit, dp[i]);
}
return profit;
}
提交结果:
其实数组是可以优化掉的
int maxProfit(vector<int>& prices) {
if(prices.size() < 2) return 0;
vector<int> dp(prices.size()-1);
dp[0] = max(0, prices[1] - prices[0]);
int profit = dp[0];
for(int i=1; i<dp.size(); i++){
dp[i] = max(0, dp[i-1] + prices[i+1] - prices[i]);
profit = max(profit, dp[i]);
}
return profit;
}
提交结果:
其实,dp数组也是可以优化掉的
int maxProfit(vector<int>& prices) {
if(prices.size() < 2) return 0;
int cur_profit = max(0, prices[1] - prices[0]);
int profit = cur_profit;
for(int i=1; i<prices.size()-1; i++){
cur_profit = max(0, cur_profit + prices[i+1] - prices[i]);
profit = max(profit, cur_profit);
}
return profit;
}
提交结果:
总结:求区间和问题和数组元素求差问题是可以相互转化的
线上提交地址:买卖股票的最佳时机 参考题解:点击跳转
二、求最大子序和
代码如下:
int maxSubArray(vector<int>& nums) {
int cur = nums[0];
int res = cur;
for(int i=1; i<nums.size(); i++){
cur = cur > 0 ? cur + nums[i] : nums[i];
if(cur > res) res = cur;
}
return res;
}
其中 cur = cur > 0 ? cur + nums[i] : nums[i] 可理解为:
- 若当前累加和cur大于0,则可以利用累加和,将当前元素与cur相加,得到更大的累加和
- 否则,舍弃累加和,从当前元素重新累加,以免使得累加和变小
注:本题应采用逆向思维理解,不是遍历到一个新元素时考虑是否将新元素加入到累加和。而是每次都从此新的元素开始累加,但是要考虑是否利用上前面的累加和。
按照此理解,买卖股票的最佳时机 代码可写为
int maxProfit(vector<int>& prices) {
if(prices.size() < 2) return 0;
int cur_profit = max(0, prices[1] - prices[0]);
int profit = cur_profit;
for(int i=1; i<prices.size()-1; i++){
int diff = prices[i+1] - prices[i];
cur_profit = cur_profit > 0 ? cur_profit + diff : diff;
if(cur_profit > profit) profit = cur_profit;
}
return profit;
}
三、最长上升子序列
首先初始化一个全1的dp数组(表示每个元素至少能独立成为一个上升子序列),表示包含0~i号元素中,包含
在内的上升序列中最长上升序列的长度
递推公式如下:
递推公式说明
,确定
时,重新遍历区间
,做如下判断:
- 当
时,此时
可以和
中的部分元素一起组成上升子序列,此时做更新:
- 当
时,此时
不可以和
中的部分元素一起组成上升子序列,不用更新,直接下一次循环
举例:
注意:dp数组可不是递增的,仔细理解上面提到的的含义
int lengthOfLIS(vector<int>& nums) {
int N = nums.size();
if(N <= 1) return N;
vector<int> dp(N, 1);
int res = 1;
for(int i=1; i<N; i++){
for(int j=0; j<i; j++){
if(nums[i] > nums[j]){
dp[i] = max(dp[i], dp[j]+1);
}
}
res = max(res, dp[i]);
}
return res;
}