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

Day43 | 动态规划 :状态机DP 买卖股票的最佳时机买卖股票的最佳时机II

Day43 | 动态规划 :状态机DP 买卖股票的最佳时机&&买卖股票的最佳时机II

动态规划应该如何学习?-CSDN博客

动态规划学习:

1.思考回溯法(深度优先遍历)怎么写

注意要画树形结构图

2.转成记忆化搜索

看哪些地方是重复计算的,怎么用记忆化搜索给顶替掉这些重复计算

3.把记忆化搜索翻译成动态规划

基本就是1:1转换

文章目录

  • Day43 | 动态规划 :状态机DP 买卖股票的最佳时机&&买卖股票的最佳时机II
    • 121.买卖股票的最佳时机
      • 思路分析(子问题):
        • 重点:
          • 如果我第i天持有股票,status==1,dfs(i,1)
          • 如果我第i天没有股票,status==0,dfs(i,0)
      • 1.回溯 DFS
      • 2.记忆化搜索
      • 3.1:1翻译为动态规划
    • 122.买卖股票的最佳时机II
      • 1.回溯法
      • 2.记忆化搜索
      • 3.动态规划

121.买卖股票的最佳时机

121. 买卖股票的最佳时机 - 力扣(LeetCode)

思路分析(子问题):

最后一天发生了什么?

输入:[7,1,5,3,6,4]
输出:5

从低0天开始到第五天结束时的利润=从第0天开始到第四天结束时的利润+第五天的利润

第五天的利润有三种可能(就光说第五天这一天)

1.我啥也没干那就是0,因为我没有买进也没有卖出

2.我把我有的股票给卖了,那就是4,即prices[5]

3.我本来就没股票,我今天买入了,那就是-4,即-prices[5]

为什么是负的利润还要买?因为你卖的前提是你买了。

然后就是熟悉的往前推,从0到第四天结束的利润是前三天的利润+第四天的直到第0天

可以发现我们需要一个bool值来体现我们现在是否持有股票,false表示没有,true表示有

大家要注意理解:

我第i-1天的结束就是第i天的开始

重点:

dfs(i,status)的含义就是我从第0天到第i天,在status状态下所能得到的最大金额,status就是咱们提到的bool值0或1

如果我第i天持有股票,status==1,dfs(i,1)

那我第一个可能是我第i-1天就有股票但是我没卖掉,即i-1天的时候也有股票,那我在从0-i这几天的最大金额就肯定是dfs(i-1,1)

也有可能是我在第i天的时候买了,那说明我前面都没买也没卖,那前i-1天的总利润就是0,第i天的利润就是-prices[i]

dfs(i,1)=max(dfs(i-1,1),-prices[i])
如果我第i天没有股票,status==0,dfs(i,0)

那我第一个可能是我第i-1天就没有股票,然后今天也没买,那我在从0到i这几天的最大金额就肯定是dfs(i-1,0)

也有可能是我在0到i天中间买了股票了,一直没卖,到了今天才卖掉了,那我第i天的利润就是dfs(i-1,1)+prices[i],因为你得保证你卖出的时候你的前一天是有股票的,不然卖啥啊对吧

(买的时候花的钱已经算到买的那天的利润里面去了,是负的)

dfs(i,0)=max(dfs(i-1,0),dfs(i-1,1)+prices[i])

最后返回的是什么,那肯定是最后一天不持有股票的情况,就是我们要的最大值了

1.回溯 DFS

1.返回值和参数

i是每天的价格

status是状态,表示是否持有股票

int dfs(int i,int status,vector<int>& prices)

2.终止条件

i<0的函数返回给了i=0的情况即第0天的利润

我们在第0天如果有股票,那肯定当天利润就是买的prices[0],如果没有股票那利润肯定就是0咯

		if(i<0)
            if(status==true)   
                return -prices[0];
            else    
                return 0;

3.本层逻辑

如递推公式所说,我有股票,那就从有股票的两种情况选个最大的

没有股票就从没有股票的两种情况选个最大的

		if(status==1)
            return max(dfs(i-1,1,prices),-prices[i]);
        else    
            return max(dfs(i-1,0,prices),dfs(i-1,1,prices)+prices[i]);

完整代码:

当然,这是超时的

class Solution {
public:
    int dfs(int i,int status,vector<int>& prices)
    {
        if(i<0)
            if(status==true)   
                return -prices[0];
            else    
                return 0;
        if(status==1)
            return max(dfs(i-1,1,prices),-prices[i]);
        else    
            return max(dfs(i-1,0,prices),dfs(i-1,1,prices)+prices[i]);
    }
    int maxProfit(vector<int>& prices) {
        return dfs(prices.size()-1,0,prices);
    }
};

2.记忆化搜索

就是搞一个哈希表dp,全都初始化为-1,每次返回前给哈希表dp赋值,碰到不是-1的那就是算过的,那就直接返回计算过的结果,不需要再次递归了

class Solution {
public:
    int dfs(int i,int status,vector<int>& prices,vector<vector<int>>& dp)
    {
        if(i<0)
            if(status==true)   
                return -prices[0];
            else    
                return 0;
        if(dp[i][status]!=-1)
            return dp[i][status];
        if(status==1)
            return dp[i][status]=max(dfs(i-1,1,prices,dp),-prices[i]);
        else    
            return dp[i][status]=max(dfs(i-1,0,prices,dp),dfs(i-1,1,prices,dp)+prices[i]);
    }
    int maxProfit(vector<int>& prices) {
        vector<vector<int>> dp(prices.size(),vector<int>(2,-1));
        return dfs(prices.size()-1,0,prices,dp);
    }
};

3.1:1翻译为动态规划

1.确定dp数组以及下标的含义

dp数组是前i天可以取得的最大利润

下标笔者采用dp的i对应prices的i-1,防止dp数组下标出现负数(影响的知识dp数组中的位置,对结果不影响的)

也会给出dp数组和prices下标对应的,即dp数组下标就是天数

2.确定递推公式

//第i天没股票
dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i]);
//第i天有股票
dp[i][1]=max(dp[i-1][1],-prices[i]);
dp[i+1][0]=max(dp[i][0],dp[i][1]+prices[i]);
dp[i+1][1]=max(dp[i][1],-prices[i]);

3.dp数组如何初始化

第0天没有股票,利润为0,dp[0][0]=0;

第0天有股票,那肯定买的第0天的,利润为-prices[i],dp[0][1]=-prices[i];

和回溯中终止条件对应

4.确定遍历顺序

后续结果需要依赖前面的计算结果,故使用从前往后遍历

		for(int i=0;i<prices.size();i++)
        {
            dp[i+1][0]=max(dp[i][0],dp[i][1]+prices[i]);
            dp[i+1][1]=max(dp[i][1],-prices[i]);
        }   

完整代码

//避免出现负数下标的情况 dp下标从1开始
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        vector<vector<int>> dp(prices.size()+1,vector<int>(2,0));
        dp[0][0]=0;
        dp[0][1]=-prices[0];
        for(int i=0;i<prices.size();i++)
        {
            dp[i+1][0]=max(dp[i][0],dp[i][1]+prices[i]);
            dp[i+1][1]=max(dp[i][1],-prices[i]);
        }   
        return dp[prices.size()][0];
    }
};
//dp下标从0开始
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int len = prices.size();
        if (len == 0) return 0;
        vector<vector<int>> dp(len, vector<int>(2));
        dp[0][0] -= prices[0];
        dp[0][1] = 0;
        for (int i = 1; i < len; i++) {
            dp[i][0] = max(dp[i - 1][0], -prices[i]);
            dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0]);
        }
        return dp[len - 1][1];
    }
};

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

122. 买卖股票的最佳时机 II - 力扣(LeetCode)

和上一题的区别就是,这里的股票可以买卖多次

体现在如果我第i天有股票的话,我可能前面买过股票

 dfs(i,1)=max(dfs(i-1,1,prices),dfs(i-1,0,prices)-prices[i]);

而上一题是

dfs(i,1)=max(dfs(i-1,1),-prices[i])

直接写-prices[i](其实是0-prices[i])是因为如果你在第i天买进,前面肯定没买过,因为上一题只能买一次,0到i-1天的最大利润只可能是0

本题是前面可能买卖过,可能0到i-1天的最大利润不为0

1.回溯法

class Solution {
public:
    int dfs(int i,int status,vector<int>& prices)
    {
        if(i<0)
            if(status==1)
                return -prices[0];
            else
                return 0;
        if(status==1)
            return max(dfs(i-1,1,prices),dfs(i-1,0,prices)-prices[i]);
        else
            return max(dfs(i-1,0,prices),dfs(i-1,1,prices)+prices[i]);
    }
    int maxProfit(vector<int>& prices) {
        return dfs(prices.size()-1,0,prices);
    }
};
//lambda
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        function<int(int,int)> dfs=[&](int i,int status)->int{
            if(i<0)
            if(status==1)
                return -prices[0];
            else
                return 0;
        if(status==1)
            return max(dfs(i-1,1),dfs(i-1,0)-prices[i]);
        else
            return max(dfs(i-1,0),dfs(i-1,1)+prices[i]);
        };
        return dfs(prices.size()-1,0);
    }
};

2.记忆化搜索

class Solution {
public:
    int dfs(int i,int status,vector<int>& prices,vector<vector<int>>& dp)
    {
        if(i<0)
            if(status==1)
                return -prices[0];
            else
                return 0;
        if(dp[i][status]!=-1)
            return dp[i][status];
        if(status==1)
            return dp[i][status]=max(dfs(i-1,1,prices,dp),dfs(i-1,0,prices,dp)-prices[i]);
        else
            return dp[i][status]=max(dfs(i-1,0,prices,dp),dfs(i-1,1,prices,dp)+prices[i]);
    }
    int maxProfit(vector<int>& prices) {
        vector<vector<int>> dp(prices.size(),vector<int>(2,-1));
        return dfs(prices.size()-1,0,prices,dp);
    }
};
//lambda
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        vector<vector<int>> dp(prices.size(),vector<int>(2,-1));
        function<int(int,int)> dfs=[&](int i,int status)->int{
            if(i<0)
            if(status==1)
                return -prices[0];
            else
                return 0;
        if(dp[i][status]!=-1)
            return dp[i][status];
        if(status==1)
            return dp[i][status]=max(dfs(i-1,1),dfs(i-1,0)-prices[i]);
        else
            return dp[i][status]=max(dfs(i-1,0),dfs(i-1,1)+prices[i]);
        };
        return dfs(prices.size()-1,0);
    }
};

3.动态规划

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        vector<vector<int>> dp(prices.size(),vector<int>(2,0));
        dp[0][0]=0;
        dp[0][1]=-prices[0];
        for(int i=1;i<prices.size();i++)
        {
            dp[i][1]=max(dp[i-1][1],dp[i-1][0]-prices[i]);
            dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i]);
        }
        return dp[prices.size()-1][0];
    }
};

http://www.kler.cn/a/391169.html

相关文章:

  • 在 Ubuntu 上安装 `.deb` 软件包有几种方法
  • 【计算机网络】Socket编程接口
  • 修改yolo格式的labels类别、删除yolo格式的labels类别
  • 笔记 | image may have poor performance,or fail,if run via emulation
  • GEE 数据集——美国gNATSGO(网格化国家土壤调查地理数据库)完整覆盖了美国所有地区和岛屿领土的最佳可用土壤信息
  • sql专题 之 sql的执行顺序
  • 020_Servlet_Mysql学生选课系统(新版)_lwplus87
  • 第 3 章 -GO语言 基本语法
  • 1Panel修改PostgreSQL时区
  • 高版本安装JAVA JDK没有JRE环境的解决办法
  • 恒创科技:什么是 RAID 3 ? RAID 3、4 和5之间有什么区别?
  • excel使用
  • 实习冲刺Day19
  • 【小程序】封装网络请求request模块
  • Pytorch如何将嵌套的dict类型数据加载到GPU
  • 【webrtc】RTX 重传包和NACK包
  • Secure Shell(SSH) 是一种网络协议
  • RDK X3 环形麦克风板录音与播放
  • STM32 设计的较为复杂的物联网项目,包括智能家居控制系统,涵盖了硬件和软件的详细设计。
  • 屏幕解析工具——OmniParser
  • vue内置方法总结
  • Qt中MainWindow的isVisible和isActiveWindow有什么区别
  • 基本和引用数据类型以及对象字面量(day14)
  • ubuntu24.04播放语音视频
  • 启动本地开发环境(自带热启动)yarn serve
  • Pytorch学习--神经网络--完整的模型验证套路