当前位置: 首页 > news >正文

数据结构与算法:动态规划中用观察优化枚举

前言

在动态规划里,有时候光写到严格位置依赖的话依然会超时,这时候可能就需要一点技巧来对动态规划进行优化。例如,多重背包问题中的二进制分组优化和单调队列优化都是动态规划优化的一种。

一、观察并优化转移方程

观察并优化转移方程基本上就是最简单的一种优化方式,主要就是通过观察对依赖的枚举,然后将枚举的循环优化成O(1)级别的方法。

1.买卖股票的最佳时机

class Solution {
public:int maxProfit(vector<int>& prices) {int n=prices.size();int ans=0;for(int i=1,Min=prices[0];i<n;i++){Min=min(Min,prices[i]);//维护左侧最小值 -> 买入ans=max(ans,prices[i]-Min);//看看此时卖出能否收益更大}return ans;}
};

其实要提的是题目四,正好就一起把股票问题都写了吧。

这个题其实就很简单了,就是每次维护左侧最小值,即买入时间,然后每次都判断在此时买入能否把收益更新得更大即可。

2.买卖股票的最佳时机 II

class Solution {
public:int maxProfit(vector<int>& prices) {//抓“上坡”int n=prices.size();int ans=0;for(int i=1;i<n;i++){if(prices[i-1]<prices[i]){ans+=prices[i]-prices[i-1];}}return ans;}
};

这个题也不难其实,就是抓“上坡”,即若下一天价值会增加就选择今天买入后明天卖出。

3.买卖股票的最佳时机 III

class Solution {
public:int maxProfit(vector<int>& prices) {//return way1(prices);//return way2(prices);//return way3(prices);return way4(prices);}//未优化枚举的解int way1(vector<int>&prices){int n=prices.size();//定义dp1[i]:0~i上发生且只发生一次交易,不一定必在i卖出vector<int>dp1(n);for(int i=1,Min=prices[0];i<n;i++){Min=min(Min,prices[i]);//左侧最小值dp1[i]=max(dp1[i-1],prices[i]-Min);//不卖和卖}//定义dp2[i]:0~i上发生两次交易且第二次必在i卖出vector<int>dp2(n);int ans=0;for(int i=1;i<n;i++){for(int j=0;j<=i;j++)//枚举第二次买入时机{dp2[i]=max(dp2[i],dp1[j]+prices[i]-prices[j]);}ans=max(ans,dp2[i]);}return ans;}//优化过枚举的解int way2(vector<int>&prices){int n=prices.size();//观察求dp2的枚举过程,可以发现,每一步都有加prices[i]//所以改为求dp1[j]+prices[j]的最大值,最后再加prices[i]//求dp1vector<int>dp1(n);for(int i=1,Min=prices[0];i<n;i++){Min=min(Min,prices[i]);//左侧最小值dp1[i]=max(dp1[i-1],prices[i]-Min);//不卖和卖}//定义best[i]:0~i范围上,dp1[i]+prices[i]最大值vector<int>best(n);//初始化best[0]=dp1[0]-prices[0];for(int i=1;i<n;i++){best[i]=max(best[i-1],dp1[i]-prices[i]);}//求dp2vector<int>dp2(n);int ans=0;for(int i=1;i<n;i++){dp2[i]=best[i]+prices[i];ans=max(ans,dp2[i]);}return ans;}//进一步优化循环的解int way3(vector<int>&prices){int n=prices.size();//因为三个for循环都是从1~n-1,所以可以合并vector<int>dp1(n);vector<int>best(n);vector<int>dp2(n);//初始化best[0]=-prices[0];int ans=0;for(int i=1,Min=prices[0];i<n;i++){Min=min(Min,prices[i]);dp1[i]=max(dp1[i-1],prices[i]-Min);best[i]=max(best[i-1],dp1[i]-prices[i]);dp2[i]=best[i]+prices[i];ans=max(ans,dp2[i]);}return ans;}//进行空间压缩的解int way4(vector<int>&prices){int n=prices.size();int dp1=0;int best=-prices[0];int ans=0;for(int i=1,Min=prices[0];i<n;i++){Min=min(Min,prices[i]);dp1=max(dp1,prices[i]-Min);best=max(best,dp1-prices[i]);ans=max(ans,best+prices[i]);}return ans;}
};

因为要求是只能交易两笔,所以考虑设置dp1和dp2,分别定义为0~i只发生一次交易和发生两次交易且第二次必须在i位置的最大收益。那么dp1的计算方法就是维护左侧最小值,然后来到i位置分卖和不卖两种情况即可。dp2的求法就是每来到i位置时要去左侧枚举第二次的买入时机,看看能不能把自己更新得更大,所以依赖关系就是dp1[j],即在0~j范围上完成一次交易,然后加上j位置买入i位置卖出的收益。最后,用ans记录在每个位置完成两次交易的最大收益。

因为这个方法时间复杂度太高了,所以要对其进行优化。进一步观察dp2的枚举过程可以发现,每一个依赖都有加i位置的价格。所以就可以考虑提出来最后统一加,那么依赖关系就转化成了求dp1[j]+prices[j]的最大值。所以可以定义best[i]为0~i范围上dp1[i]+prices[i]的最大值。那么有了best数组,求dp2的时候就直接再best的基础上加上prices[i]即可。

再观察可以发现,因为这三个循环都是从1到n-1的,所以可以考虑合并到一起求。

再观察依赖关系可以发现,可以进行空间压缩,那么就是用简单几个变量往下滚动更新。

4.

相关文章:

  • 【520特辑】情人节脑影像绘图
  • 更新2011-2025经济类联考 396-真题+解析 PDF
  • Hutool 常用工具类实战指南
  • 【C++】C++的拷贝构造函数介绍使用
  • Java双指针法:原地移除数组元素
  • Unreal5 从入门到精通之如何实现 离线语音识别
  • 【BIO、NIO、AIO的区别?】
  • 05 接口自动化-框架封装思想建立之httprunner框架(中)
  • 目标检测DINO-DETR(2023)详细解读
  • 海康工业相机白平衡比选择器对应的值被重置后,如何恢复原成像
  • 【Code】Foundations 2017- Catalogue, List of Tables, List of Figures
  • iOS Runtime与RunLoop的对比和使用
  • Journal of Real-Time Image Processing 投稿过程
  • 区域双碳治理:数据驱动与系统破局之道
  • 2.4.1死锁的概念
  • 计算机网络通信技术与协议(七)———关于ACL的详细解释
  • 迪菲-赫尔曼密钥交换算法深度解析
  • 重构研发效能:项目管理引领软件工厂迈向智能化
  • 第二届帕鲁杯screenshot
  • 【Linux】第二十一章 管理存储堆栈
  • 重庆黔江一足疗养生馆负责人涉嫌违法犯罪被移送检察机关
  • 围绕加快科创中心建设,上海市委中心组学习会听取专题辅导报告
  • 上影节官方海报公布:电影之城,每一帧都是生活
  • 遇见东方:18世纪俄罗斯宫殿中的“中国风”
  • 专访|金七猫奖得主:以非遗为舟,在现实题材中疗愈与成长
  • 宫崎骏的折返点