【买卖股票系列问题 -- 动规 + 其他转换方法】

前言

打怪升级:第80天

买卖股票的最佳时机

买卖股票的最佳时机:简单
这里是引用

题解1:找出最值区间

class Solution {
public:
    int maxProfit(vector<int>& prices) {
		int n=prices.size();
    	int max=prices[0];
    	int min=prices[0];
    	int sum=0;
    	for(int i=1; i<n; ++i)
    	{
    		if(prices[i] < min)	 max=min=prices[i];// 最大值需要在最小值之后 
    		if(prices[i] > max)  max=prices[i]; 
            if(max-min>sum) sum=max-min;
		}
		
		return sum;
    }
};

这里是引用

题解2:问题转化:最大子序和

class Solution {
public:
    int maxProfit(vector<int>& prices) {
		int ret=0;
		int sum=0;
		
		for(int i=1; i<prices.size(); ++i) // 转换为求解最大子序和
		{
			int dev = prices[i] - prices[i-1];
			sum = max(dev, dev + sum);
			ret = max(ret, sum);
		}
		
		return ret; 
    }
};

这里是引用

题解3:动态规划

在这里插入图片描述

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
       vector<vector<int> >dp(n, vector<int>(2, 0));
       dp[0][0] = -prices[0];

       for(int i=1; i<n; ++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[n-1][1];
    }
};

这里是引用


买卖股票的最佳时机 II

买卖股票的最佳时机 II

题解1:动态规划

这里是引用

这里是引用

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int  n = prices.size();
        vector<vector<int> >dp(n, vector<int>(2, 0));
        dp[0][0] = -prices[0];

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

        return dp[n-1][1];
    }
};

在这里插入图片描述

对比两道题的状态转移方程:
这里是引用

题解2:贪心

这道题允许我们多次买卖,只要有的赚就可以买,这里有很明显的贪心思想,下面我们画图理解:
在这里插入图片描述在这里插入图片描述

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int ret=0;
        for(int i=1; i<prices.size(); ++i)
         ret += max(0, prices[i] - prices[i-1]);  // 今天卖出前一天的股票,收益为正

         return ret;
    }
};

这里是引用
在很多情况下,贪心都是一种很便利快速的解决问题的方法,不过想要看出这道题是否可以使用贪心,这对于我们还是有很大挑战的。


买卖股票的最佳时机含冷冻期

买卖股票的最佳时机含冷冻期

动态规划

这里是引用

买入:今天手里有股票,可以是昨天的没有卖,也可以是今天新买的;
可交易:现在手中没有股票,可以是今天没有买,也可以是冷冻期刚刚结束;
冷冻期:说明今天卖出了股票,进入一天冷冻期;

状态很多的情况下,画出状态机进行分析:

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        vector<vector<int> >dp(n, vector<int>(3, 0)); // 三种状态 -- 买入、可交易、冷冻期
        dp[0][0] = -prices[0];

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

        return max(dp[n-1][1], dp[n-1][2]);
    }
};

这里是引用


买卖股票的最佳时机含手续费

买卖股票的最佳时机含手续费

动态规划

这里是引用在这里插入图片描述

class Solution {
public:
    int maxProfit(vector<int>& prices, int fee) {
        int n = prices.size();
        vector<vector<int> >dp(n, vector<int>(2, 0));
        dp[0][0] = -prices[0];
// 不要纠结于会出现一只股票卖出多次的情况,既会减去多次fee,因为这里只是赋值,实际的dp[i-1][0] 和 prices[i]都没有改变,因此再次售卖也只会减去一次fee
        for(int i=1; i<n; ++i)
        {
            dp[i][0] = max(dp[i-1][0], dp[i-1][1] - prices[i]);
            dp[i][1] = max(dp[i-1][1], dp[i-1][0] + prices[i] - fee); // 卖出的时候付手续费 -- 也可以买入的时候付
        }

        return dp[n-1][1];
    }
};


这里是引用


买卖股票的最佳时机 III

买卖股票的最佳时机 III

在这里插入图片描述

动态规划

在这里插入图片描述

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        vector<vector<int>>f(n, vector<int>(3, -prices[0])); // 买入状态
        vector<vector<int>>g(n, vector<int>(3, 0));          // 卖出状态

        for(int i=1; i<n; ++i)
        {
            for(int j=0; j<3; ++j)
            {
                f[i][j] = max(f[i-1][j], g[i-1][j] - prices[i]);
                g[i][j] = g[i-1][j];
                if(j > 0) // 判断j是否合理
                    g[i][j] = max(g[i-1][j], f[i-1][j-1] + prices[i]);
            }
        }

        return max(g[n-1][0], max(g[n-1][1], g[n-1][2]));
    }
};

这里是引用


买卖股票的最佳时机 IV

买卖股票的最佳时机 IV

这里是引用
本题与上一题的唯一区别是:上一题最多交易2次,本题最多交易k次,我们只需要将上一题中的 3 改为 k + 1即可。

动态规划

class Solution {
public:
    int maxProfit(int k, vector<int>& prices) {
        int n = prices.size();
        vector<vector<int> >f(n, vector<int>(k+1, -prices[0]));
        vector<vector<int> >g(n, vector<int>(k+1, 0));

        for(int i=1; i<n; ++i)
        {
            for(int j=0; j<k+1; ++j)
            {
                f[i][j] = max(f[i-1][j], g[i-1][j] - prices[i]);
                g[i][j] = g[i-1][j];
                if(j > 0)
                    g[i][j] = max(g[i-1][j], f[i-1][j-1] + prices[i]);
            }
        }

        int ret = 0;
        for(auto e : g[n-1]) // 最后一行取最大值
        ret = max(ret, e);

        return ret;
    }
};

这里是引用


总结

  1. 买卖股票问题与打家劫舍类问题一样,都属于多状态dp问题,既在第 i 个位置时,会有多种状态需要分析,例如:
    买卖股票:第 i 天 可以选择 持有股票,也可以不持有股票,
    打家劫舍:第 i 家 可以选择 进行偷窃,也可以不进行偷窃。
    甚至类似上面的,在第 i 天 时 一共进行了 多少次交易,这就需要再增加更多的状态表示进行区分。

  2. 上方我们在进行转移方程推导时画了好多图,例如:
    在这里插入图片描述
    这个状态转移图我们称之为:状态机,用来更加清楚地描述各个状态之间相互转换的情况,我们在之后写动态规划类题目时大都可以画出状态机来理清关系。

  3. dp[i][0] 只是考虑会买入, dp[i][1]只是考虑会卖出,实际上是否卖出要选择最大值。



  • 16
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值