[LeetCode ] Best Time to Buy and Sell Stock系列题解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/GYH0730/article/details/84451484

这一系列的题目的基本模型是给出n天每天股票的价格,可以在任意一天买股票,在之后的任意一天把股票卖出,赚取差价,限制是手中最多有一张股票,必选把手中的股票卖掉才能再买股票。求最大获利。

题目1:Best Time to Buy and Sell Stock

这是最简单的一道题,在基本模型的基础上添加了一个限制条件,最多交易一次。

我们从第一天开始遍历,维护一个已经遍历过的股票的最小价格minPrice,答案为max(prices[ i ] - minPrice)。

Java代码:

class Solution {
     public int maxProfit(int[] prices) {
		 if(prices.length == 0) return 0;
		 int res,minPrice;
		 res = 0;
		 minPrice = prices[0];
		 for(int i = 1; i < prices.length; i++) {
			 res = Math.max(res, prices[i] - minPrice);
			 minPrice = Math.min(minPrice, prices[i]);
		 }
		 return res;
	 }
}

题目2: Best Time to Buy and Sell Stock II

在基本模型的基础上,交易的次数不受限制。

既然交易的次数不受限制,我们可以贪心的来思考了,只要第i天股票的价格大于第i-1天的股票价格,我们就进行交易,这样最后得到的结果肯定是最优的。

Java代码:

class Solution {
   public int maxProfit(int[] prices) {
		 if(prices.length == 0) return 0;
		 int res;
		 res = 0;
		 for(int i = 1; i < prices.length; i++) {
			 if(prices[i] > prices[i - 1]) {
				 res += prices[i] - prices[i - 1];
			 }
		 }
		 return res;
	 }
}

题目3:Best Time to Buy and Sell Stock III

在基本模型的基础上,最多交易2次。

两次交易肯定不会交叉,即前i天发生第一次交易,第二次交易肯定会在第i天到第n天中发生。用t1[ i ]表示第0天到第i天最多交易一次的最大获利,t2[ i ]表示从第i天到第n-1天的最多交易一次的最大获利,则答案可表示为max(t1[ i ] + t2[ i ] ),至于t1[ i ],t2[ i ]如何得到可以参考题目1的方法。

Java代码:

class Solution {
    public int maxProfit(int[] prices) {
        if(prices.length == 0) return 0;
		int res,minPrice,maxPrice,MaxProfit;
		int[] t1 = new int[prices.length];
		int[] t2 = new int[prices.length];
		t1[0] = MaxProfit = 0;
		minPrice = prices[0];
		for(int i = 1; i < prices.length; i++) {
			MaxProfit = Math.max(MaxProfit, prices[i] - minPrice);
			minPrice = Math.min(minPrice, prices[i]);
			t1[i] = MaxProfit;
		}
		t2[prices.length - 1] = MaxProfit = 0;
		maxPrice = prices[prices.length - 1];
		for(int i = prices.length - 1; i >= 0; i--) {
			MaxProfit = Math.max(MaxProfit, maxPrice - prices[i]);
			maxPrice = Math.max(maxPrice, prices[i]);
			t2[i] = MaxProfit;
		}
		res = 0;
		for(int i = 0; i < prices.length; i++) {
			res = Math.max(res, t1[i] + t2[i]);
		}
		return res;
	}
}

题目4:Best Time to Buy and Sell Stock IV

这道题实在基本模型的基础上,最多交易k次。

首先我们考虑当k无限大时,就和题目2一样了,我们可以先用题目二的方法解出来需要交易的次数num,如果k>=num,直接返回按照题目二得到的结果。如果k<num,就需要我们向动态规划上考虑了。

按照常规的dp思路dp[ i ][ j ]表示在第i天交易j次的最大收益 ,转移方程为:

dp[i][j]=max\ (dp[i-1][j-1]+diff , \right dp[i-1][j] )

diff= prices[i]-prices[i-1]

但这样其实是不对的,如果在第i天和第i-1天同时有交易发生,这两次交易可以合并为1次交易,而上述方法则把它们算为两次交易,所以我们得到的最大收益就会变小了。

正确思路是用一个局部最优解local[ i ][ j ]和一个全局最优解global[ i ] [ j ]。

local[ i ][ j ]表示前i天进行j次交易,并且第j次交易发生在第i天的最大收益。

global[ i ] [ j ]表示前i天进行j次交易,但第j次交易并不一定发生在第i天的最大收益。转移方程为:

local[i][j]= max\left ( globla[i-1][j-1]+max(diff,0) , \right local[i-1][j] + diff )

global[i][j]=max(globla[i - 1][j],local[i][j])

其中局部最优值是比较前一天并少交易一次的全局最优加上大于0的差值,和前一天的局部最优加上差值后相比,注意前一天的局部交易了j次,加上差值还是为j次,这就避免了第一种方法的那种错误出现,并且diff无论正负都得加上(想想local的表示含义就理解了),两者之中取较大值,而全局最优比较局部最优和前一天的全局最优。

Java代码,当然可以利用01背包那种思想节省一下内存。

class Solution {
    public int maxProfit(int k, int[] prices) {
        if(prices.length == 0) return 0;
		int res,num;
		res = num = 0;
		for(int i = 1; i < prices.length; i++) {
			if(prices[i] > prices[i - 1]) {
				res += prices[i] - prices[i - 1];
				num++;
			}
		}
		if(num <= k) return res;
		int[][] global = new int[prices.length][k + 1];
		int[][] local = new int[prices.length][k + 1];
		for(int i = 1; i < prices.length; i++) {
			for(int j = 1; j <= k; j++) {
				int diff = prices[i] - prices[i - 1];
				local[i][j] = Math.max(global[i - 1][j - 1] + Math.max(diff, 0),local[i - 1][j] + diff);
				global[i][j] = Math.max(local[i][j],global[i - 1][j]);
			}
		}
		return global[prices.length - 1][k];
    }
}

题目5:Best Time to Buy and Sell Stock with Cooldown

在基本模型的基础上,添加了在第i天卖掉股票后的一天不能再买的限制。

整个过程中有三种状态:

  • s0:未持有有股票,可以购买股票
  • s1:持有股票,当然可以出售
  • s2:未持有股票,不可购买股票

三种状态之间的转移

这里写图片描述

Java代码:

class Solution {
    public int maxProfit(int[] prices) {
		 if(prices.length == 0) return 0;
		 int[] hasButNotBuy = new int[prices.length];
		 int[] notHasButCanBuy = new int[prices.length];
		 int[] notHasAndNotBuy = new int[prices.length];
		 hasButNotBuy[0] = -prices[0];
		 for(int i = 1; i < prices.length; i++) {
			 hasButNotBuy[i] = Math.max(hasButNotBuy[i - 1], notHasButCanBuy[i - 1] - prices[i]);
			 notHasButCanBuy[i] = Math.max(notHasButCanBuy[i - 1], notHasAndNotBuy[i - 1]);
			 notHasAndNotBuy[i] = hasButNotBuy[i] + prices[i];
		 }
		 return Math.max(notHasButCanBuy[prices.length - 1], notHasAndNotBuy[prices.length - 1]);
	 } 
}

题目6:Best Time to Buy and Sell Stock with Transaction Fee

在基本模型的基础上,每一次交易都有手续费。

整个过程有两种状态,持有股票,未持有股票。

dp[ i ][ 0 ]表示在第i天未持有股票的最大收益,dp[ i ][ 1 ]表示在第i天持有股票的最大收益,转移方程为:

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

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

Java代码,当然也可以降维。

public class Solution {
	  	public int maxProfit(int[] prices, int fee) {
	  		if(prices.length == 0) return 0;
	        int[][] dp = new int[prices.length][2];
	        dp[0][1] = -prices[0];
	        for(int i = 1; i < prices.length; i++) {
	        	dp[i][1] = Math.max(dp[i - 1][0] - prices[i], dp[i - 1][1]);
	        	dp[i][0] = Math.max(dp[i - 1][1] + prices[i] - fee, dp[i - 1][0]);
	        }
	        return dp[prices.length - 1][0];
	    }
}

猜你喜欢

转载自blog.csdn.net/GYH0730/article/details/84451484