leetcode股票系列题目——动态规划

总结:

  • 前四道都可以用二维DP,定义状态第一维代表当前天数,第二维代表当天持有股票和不持有股票两种状态,dp[ i ] [ 0 ] 代表第 i + 1 天(下标是从0开始) 不持有股票的状态,dp[ i ] [ 1 ] 表示第 i + 1 天持有股票的状态时收益最大。
  • 状态转移,当天不持有股票可以从前一天就不持有的状态转移过来,也可以是前一天持有但是现在卖出的状态转移过来,当天持有股票的状态可以从前一天就持有状态转移,也可以是前一天不持有股票但是现在买入的状态转移过来。
  • 求最大收益,就取以上状态中的最大值即可,最大收益在最后一天的卖出状态取得

121. 买卖股票的最佳时机(一次交易)

class Solution {
public:
 int maxProfit(vector<int>& prices) {
    	int n = prices.size();
    	vector<vector<int>> dp(n, vector<int>(2,0));
        //初始化baseCase,第1天不持有股票的最大收益为0,第1天持有股票的最大收益(在第一天买入)
    	dp[0][0] = 0, dp[0][1] = -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], -prices[i]);  //只允许一次交易,之前的交易收益视为0
	}
    	return dp[n-1][0];
    }
};

122. 买卖股票的最佳时机2 (多次交易)

class Solution {
public:
    int maxProfit(vector<int>& prices) {
    	int len = prices.size();
    	vector<vector<int>> dp(len,vector<int>(2,0));
	dp[0][0] = 0, dp[0][1] = -prices[0];
	for(int i = 1;i < len;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[len-1][0];
    }
};

309. 最佳买卖股票时机含冷冻期(在上一次交易后需要等待一天才能购入)

class Solution {
public:
    int maxProfit(vector<int>& prices) {
    	int n = prices.size();

        if(n < 2){  //因为初始化前两天的状态,所以需要考虑prices长度小于2
            return 0;
        }
    	int dp[n][2];    //0 - 不持有股票,1 - 持有股票 
    	//初始化前两天的baseCase
    	dp[0][0] = 0;
	dp[0][1] = -prices[0];
	dp[1][0] = max(dp[0][0], dp[0][1] + prices[1]);
	dp[1][1] = max(dp[0][1], -prices[1]); //第二天购入只能是第一天购入或者第二天购入
		
	for(int i = 2;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-2][0] - prices[i]);  
	} 
	return dp[n-1][0];
    }
};

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

class Solution {
public:
    int maxProfit(vector<int>& prices, int fee) {
    	int n = prices.size();
    	int dp[n][2];  
    	dp[0][0] = 0;
    	dp[0][1] = -prices[0];
    	for(int i = 1;i < n;i++){
    		dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i] - fee);//一次交易减去手续费 
    		dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i]);
	} 
    	
    	return dp[n-1][0];
    }
};

后面两题限制交易次数,所以需要再加一维表示次数,但是对于baseCase初始化边界暂时还不是很理解,这里只记录了自己认为比较好理解的方法

123. 买卖股票的最佳时机3(限制交易两次)

思路:考虑交易两次所有的状态,使用变量记录

  • 交易两次一共有5种状态,不操作(free)、第一次买入(buy1)、第一次卖出(sell1)、第二次买入(buy2)、第二次卖出(sell2),但是由于不操作对于收益为0,所以可以简单考虑后面四种状态

  • 将 i - 1天的这四种状态,如何转移到第i天,

    buy1_i : 第 i - 1 天第一次买入(buy1_i-1)之后没有操作, 或者第 i 天 才第一次买入

    sell1_ i : 第 i - 1 天第一次卖出(sell1_i-1)之后没有操作, 或者第 i - 1 天第一次买入第 i 天第一次卖出

    buy2_i : 第 i - 1天第二次买入(buy2_i-1)之后没有操作, 或者第 i - 1天第一次卖出后第 i 天第二次买入

    sell2_i : 第 i - 1 天第二次卖出(sell2_i-1)之后没有操作, 或者第 i - 1天第二次买入后第 i 天第二次卖出

  • 最大收益由于始终维护的是最大值,并且同一天买入和卖出不影响收益这一宽松条件使得,sell2处获得的是最终的最大收益

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        int buy1 = -prices[0];
        int sell1 = 0;
        int buy2 = -prices[0];
        int sell2 = 0;
        for(int i = 0;i < n;i++){
            buy1 = max(buy1, -prices[i]);
            sell1 = max(sell1, buy1 + prices[i]);
            buy2 = max(buy2, sell1 - prices[i]);
            sell2 = max(sell2, buy2 + prices[i]);
        }
        return sell2;
    }
};

188. 买卖股票的最佳时机4(限制K次交易)

思路:考虑k次交易可能存在的所有状态

  • 进行k次交易总共存在2*k + 1次状态(无操作,第k次买入,第k次卖出 = 1 + k + k)

  • 将状态分为两大类(买入和卖出),[1 - 2*k]中 奇数次代表买入,偶数次代表卖出,初始化baseCase,第一天的状态中买入操作初始化为 -prices[0],卖出操作和不操作收益都为0

  • 第 i 天的状态由第 i - 1天的状态转移而来,

    第 i 天买入可以是第 i - 1 天的买入(不操作) 或者 第 i - 1 天的卖出后买入

    第 i 天卖出可以是第 i - 1 天卖出(不操作) 或者 第 i - 1天买入后卖出

  • dp中偶数次维护的是截至到第 i 天 第 k 次交易(卖出)最大收益,最终最大收益 = dp[2 * k]

class Solution {
public:
    int maxProfit(int k, vector<int>& prices) {
        int n = prices.size();
        
	k = min(k, n/2);  //prices天数不够K次交易的情况,取k 和 n/2 中的最小值 
        
        vector<int> dp(2*k+1,0);  //0代表不操作,奇数代表买,偶数代表卖
       
	    //初始化第1天状态的baseCase
        for(int i = 1;i < 2*k+1;i++){
            if(i % 2){
                dp[i] = -prices[0];  //第1天的所有奇数次状态买入,偶数次卖出收益0 
            }
        }
        for(int i = 1;i < n;i++){
            for(int j = 1;j < 2*k+1;j++){
                if(j % 2 != 0){
                    dp[j] = max(dp[j], dp[j-1] - prices[i]);  //买入 
                }else{
                    dp[j] = max(dp[j], dp[j-1] + prices[i]);  //卖出 
                }
            }
        }
        return dp[2*k];
    }
};
posted @ 2021-04-30 19:40  简约的信仰  阅读(81)  评论(0编辑  收藏  举报