LeetCode动态规划专题

一、买卖股票的最佳时机

在这里插入图片描述
虽然这题标记的是简单,个人感觉还是挺难的。。。也许是我太菜了吧,找了题解才会做,看了大佬的题解后,发现这是一种题型,真是醍醐灌顶。觉得应该总结一下,作为自己的刷题笔记。下面进入正文。

老司机一眼可以看出,这种求差问题可以转换成区间和的问题(emmmmm,我肯定看不出来)。为什么呢?因为我们有牛顿莱布尼茨公式:

∫ a b f ( x ) d x = F ( x ) ∣ a b = F ( b ) − F ( a ) \qquad\qquad\qquad\int_a^bf(x)dx=F(x)|_a ^b=F(b)-F(a) abf(x)dx=F(x)ab=F(b)F(a)

在上面的公式中, F ( ) F() F()表示的数组称为前缀和,其实也就是题目所给的当天股票价格,最大的区间和可用动态规划求解,公式如下:

d p [ i ] = m a x ( 0 , d p [ i − 1 ] + d i f f [ i ] ) \qquad\qquad\qquad dp[i]=max(0, dp[i-1]+diff[i]) dp[i]=max(0,dp[i1]+diff[i])

其中, d p [ i ] dp[i] dp[i]表示在不亏钱的前提下,第 i i i天卖出股票所赚的钱,若 d p [ i ] dp[i] dp[i]等于0了,则说明第 i i i天卖股票亏了,将 d p [ i ] dp[i] dp[i]重新置0。 d i f f diff diff数组表示相邻两天股票价格的差

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;
    }

提交结果:
在这里插入图片描述

其实 d i f f diff diff数组是可以优化掉的

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数组(表示每个元素至少能独立成为一个上升子序列), d p [ i ] dp[i] dp[i]表示包含0~i号元素中,包含 n u m s [ i ] nums[i] nums[i]在内的上升序列中最长上升序列的长度

递推公式如下:
d p [ i ] = m a x ( d p [ i ] , d p [ j ] + 1 ) \qquad\qquad\qquad dp[i] = max(dp[i], dp[j]+1) dp[i]=max(dp[i],dp[j]+1)
递推公式说明

j ∈ [ 0 , i ) j\in[0,i) j[0,i),确定 d p [ i ] dp[i] dp[i]时,重新遍历区间 [ 0 , i ) [0,i) [0,i),做如下判断:

  • n u m s [ i ] > n u m s [ j ] nums[i] > nums[j] nums[i]>nums[j]时,此时 n u m s [ i ] nums[i] nums[i]可以和 [ 0 , j ] [0,j] [0,j]中的部分元素一起组成上升子序列,此时做更新: d p [ i ] = m a x ( d p [ i ] , d p [ j ] + 1 ) dp[i] = max(dp[i], dp[j]+1) dp[i]=max(dp[i],dp[j]+1)
  • n u m s [ i ] < = n u m s [ j ] nums[i] <= nums[j] nums[i]<=nums[j]时,此时 n u m s [ i ] nums[i] nums[i]不可以和 [ 0 , j ] [0,j] [0,j]中的部分元素一起组成上升子序列,不用更新,直接下一次循环

举例:
在这里插入图片描述
注意:dp数组可不是递增的,仔细理解上面提到的 d p [ i ] dp[i] dp[i]的含义

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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bugcoder-9905

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值