动态规划:前缀最值

7 篇文章 1 订阅

前缀最值

什么?动态规划还能计算股票的最大利润?学好动态规划就能炒股了吗,那我倒要看看是怎么回事。

题干

给定一个数组prices,prices[i]表示股票i天的价格,只能在某天买入并在未来某一天卖出,计算你所能获得的最大利润。

题目分析

emm,需要怎么说吗,像这种只要答案不需要给出过程的题目,八九不离十,就是动态规划了。我太懂了。。

动态规划题目的解题步骤:

  1. 定义最优状态数组dp = []
  2. 确定状态转移方程dp[i]=xxxx
  3. 递推出最终答案result

解题的根本思路就是:从下标0开始找到每个下标为止(不包括当前下标)的最小值,先找出每个下标的最小值。接着找每个最小值右边的最大值。两者相减得到的就是最大利润。取最大值就是我们要的答案

解题步骤

  1. 定以最佳状态,dp[],dp[i]表示xx表为i及之前的数值的最小值

  2. 计算前缀最小值

    如何计算?

    如果只有1位,前面没有数,那么它就是最小值,即:dp[0]=prices[0]

    如果是有2位,前面有1位数,那么最小值就是当前值比较前一位为止的最小值,即dp[1]=min(dp[1-1],prices[1])

    如果是有3位,那么最小值也是当前值比较前一位为止的最小值,即dp[2]=min(dp[2-1],prices[2])

  3. 定义一个结果最大值,再遍历一遍价格,用当前价格减去当前下标前的最小值,取遍历结果的最大值就是我们要的答案

代码实现(js)

function sharesBisnes(){
    // 股票的最佳买卖时机
    /* 前缀最值
		 给定一个数组prices,prices[i]表示股票i天的价格,只能在某天买入并在未来某一天卖出,计算你所能获得的最大利润
		*/
    let prices =[7,1,5,3,6,4] // 结果为:5 ;i:1和4
    // 算出前面i天的最低价,记为dp[i]
    dp = []
    prices.forEach((item,i)=>{
        if(i == 0){
            dp[i] = item
        }else{
            dp[i] = min(dp[i-1],item)
        }
    })
    // 用第i天前最低价减去第i天价格,同时记录最大值,就得到最大利润了
    ans = 0;
    prices.forEach((item,i)=>{
        ans = max(item-dp[i],ans)
    })
    document.write("最大利润为"+ans)
}
function max(a,b){
    return a > b ? a : b
}

总结

求前缀最值其实比较简单,这篇文废话也相对比较多。本文是包括了计算前缀最值,但不是直接用前缀最值来解答。

前缀最值就是求到i为止的最大值或者最小值,用遍历的方式同样可以实。但是用动态规划的思想,前一位数已经算出最小值了,那么当前这位数的最值,要么就是前一位的最值要么就是本身咯,这样往后推,不管数组多长,我们的时间复杂度一直是一个常数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
树状数组可以用来维护一个序列的前缀和,从而实现快速区间查询。但是如果想要求区间最大值或最小值,需要进行一些修改。 具体做法如下: 1. 定义一个数组 $maxn$,表示每个节点的最大值。 2. 在更新某个节点的值时,同时更新其父节点的最大值。 3. 在查询区间最大值时,从右往左遍历每个节点,找到第一个其右边的兄弟节点的最大值大于等于查询区间左端点的位置,然后从该兄弟节点开始往右遍历,找到第一个其左边的兄弟节点的最大值小于等于查询区间右端点的位置。这两个位置之间的节点的最大值即为查询区间的最大值。 示例代码: ```cpp const int N = 1e5; int a[N], c[N], maxn[N]; void update(int x, int v) { a[x] = v; while (x <= N) { c[x] += v; maxn[x] = max(maxn[x], v); x += x & -x; } } int query(int l, int r) { int res = INT_MIN; for (int i = r; i >= l; ) { if (i - (i & -i) + 1 >= l) { res = max(res, maxn[i]); i -= i & -i; } else { res = max(res, a[i]); i--; } } return res; } ``` 其中,$a$ 数组表示原始序列,$c$ 数组表示树状数组,$maxn$ 数组表示每个节点的最大值。在更新节点 $x$ 的值时,同时更新 $maxn$ 数组。在查询区间最大值时,从右往左遍历每个节点,找到第一个其右边的兄弟节点的最大值大于等于查询区间左端点的位置,然后从该兄弟节点开始往右遍历,找到第一个其左边的兄弟节点的最大值小于等于查询区间右端点的位置。这两个位置之间的节点的最大值即为查询区间的最大值

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

jayLog

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

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

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

打赏作者

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

抵扣说明:

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

余额充值