动态规划和滚动数组

    题目大致意思是这样的:给你一个序列的股票价格,你要在合理的时候买入,合理的时候卖出,使其收益最大。

    这里如果是直接暴力解决的话,从第k天买入,则卖出有n-k天可以选择,最终时间复杂度为O(n^2)。如果天数量级上去了,就有点难以接受了。

    我这里使用动态规划来做,假设dp[i]表示第i天卖出能得到的最高收益,那么如果第i天不卖,选择在第i+1天卖出,而买入时间不变,能够得到的收益为:p[i+1]-dp[i]+dp[i]。但是这有可能会是一个负值,如果是负值而且在第i+1天卖出,我们可以选择不在之前时间买入,而选择在第i+1天买入再卖出,这样的收益为0.我们需要最大化这个收益,故最大收益为这个过程中的计算的所有结果里面最大的。

代码如下:
import numpy as np
p = [100,113,110,85,105,102,86,63,81,101,94,106,101,79,94,90,97]
length = len(p)
Max = 0
dp = np.zeros(length)
for i in range(1,length):
    dp[i] = max(p[i]-p[i-1]+dp[i-1],0)
    if(dp[i] > Max):
        Max = dp[i]
print(Max)
优化

    在上面代码里,我们使用了一个长度为n的数组存储每次计算的收益,但实际上我们可以发现,算当天收益仅仅需要前一天卖出时收益这一个额外变量;并且前一天卖出的收益在这里使用一次后便没有用了。故我们可以考虑使用滚动数组,把空间复杂度压缩到常数级。

改良后的代码如下:
import numpy as np
p = [100,113,110,85,105,102,86,63,81,101,94,106,101,79,94,90,97]
length = len(p)
Max = 0
dp = 0 #使用滚动数组
for i in range(1,length):
    dp = max(p[i]-p[i-1]+dp,0)
    if(dp > Max):
        Max = dp
print(Max)

    在改良的代码中,我们仅仅使用了一个dp的变量去记录若上一天卖出能得到的收益,并迭代找到最大的一个。如果你想要知道得到最大收益是需要什么时候买进和卖出,也只需要多添加两个变量在修改max的时候去记录而已。

### 完全背包问题中的滚动数组优化 在解决完全背包问题时,动态规划是一种常用的方法。为了减少内存消耗并提高效率,可以引入滚动数组来优化标准的动态规划实现。 #### 基础概念回顾 对于给定的一组物品及其价值和重量,在不超过总容量的情况下求解能够获得的最大价值的问题称为背包问题。当每种类型的物品数量无限多时,则成为完全背包问题[^1]。 #### 使用一维DP表代替二维表格 传统上,处理此类问题是通过构建一个二维`dp[][]`矩阵完成的;然而这会占用较多存储资源。因此可以通过仅保留当前层与前一层的数据来进行迭代更新,从而达到节省空间的目的: - `dp[j]`: 表示容量为`j`时所能得到的最大价值。 初始化阶段设置所有位置初始值均为0,并遍历每一个可能放入背包内的物件,按照如下方式调整状态转移方程: ```cpp for (int i = 0; i < n; ++i) { for (int j = w[i]; j <= V; ++j) { dp[j] = max(dp[j], dp[j-w[i]] + c[i]); } } ``` 这里的关键在于内循环是从左向右执行(`j=w[i]->V`)而非相反方向,这是因为每个元素可以在同一轮次被多次选取[^3]。 #### 进一步改进:倒序枚举 尽管上述方法已经实现了显著的空间复杂度降低,但在某些情况下还可以继续简化逻辑结构。具体做法是在内部循环中采取逆序访问策略,即从大到小依次计算各个子问题的结果。这样做不仅保持了原有的时间性能优势,而且使得代码更加简洁易懂[^4]: ```cpp void DP(int v, int m){ //m个物品,背包体积为v for(int i=1;i<=m;++i){ for(int j=v;j>=c[i];--j){ f[j]=max(f[j],f[j-c[i]]+w[i]); } } } ``` 此版本避免了额外开辟临时变量的需求,进一步减少了程序运行期间所需的堆栈空间开销。 综上所述,通过对原始算法进行适当改造,特别是借助于滚动数组技巧以及合理的循环控制机制,能够在很大程度上改善针对完全背包类题目所设计解决方案的整体表现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值