309. Best Time to Buy and Sell Stock with Cooldown(Medium)
题目描述
给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。
设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):
你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
示例
输入: [1,2,3,0,2]
输出: 3
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]
思路
确定状态:持有、不持有且在冷冻期、不持有且不在冷冻期
确定状态转移方程:
dp[i][0] = Math.max(dp[i-1][0],dp[i-1][2]-prices[i]);
dp[i][1] = dp[i-1][0]+prices[i];
dp[i][2] = Math.max(dp[i-1][1],dp[i-1][2]);
class Solution {public int maxProfit(int[] prices) {if(prices==null || prices.length==0){return 0;}int len = prices.length;int[][] dp = new int[len][3];dp[0][0] = -prices[0];dp[0][1] = 0;dp[0][2] = 0;for(int i=1; i<len; i++){dp[i][0] = Math.max(dp[i-1][0],dp[i-1][2]-prices[i]);dp[i][1] = dp[i-1][0]+prices[i];dp[i][2] = Math.max(dp[i-1][1],dp[i-1][2]);}return Math.max(dp[len-1][1],dp[len-1][2]);}}
内存优化
思路
每次仅用到上一次的数据。
class Solution {public int maxProfit(int[] prices) {if(prices==null || prices.length==0){return 0;}int len = prices.length;int[] dp = new int[3];dp[0] = -prices[0];dp[1] = 0;dp[2] = 0;for(int i=1; i<len; i++){int tmp = dp[0];dp[0] = Math.max(dp[0],dp[2]-prices[i]);dp[2] = Math.max(dp[1],dp[2]);dp[1] = tmp + prices[i];}return Math.max(dp[1],dp[2]);}}
714. Best Time to Buy and Sell Stock with Transaction Fee (Medium)
题目描述
给定一个整数数组 prices,其中第 i 个元素代表了第 i 天的股票价格 ;非负整数 fee 代表了交易股票的手续费用。
你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。
返回获得利润的最大值。
注意:这里的一笔交易指买入持有并卖出股票的整个过程,每笔交易你只需要为支付一次手续费。
示例
输入: prices = [1, 3, 2, 8, 4, 9], fee = 2
输出: 8
解释: 能够达到的最大利润:
在此处买入 prices[0] = 1
在此处卖出 prices[3] = 8
在此处买入 prices[4] = 4
在此处卖出 prices[5] = 9
总利润: ((8 - 1) - 2) + ((9 - 4) - 2) = 8.
思路
确定状态:持有、不持有。
确定状态转移:dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1]-prices[i]);
dp[i][1] = Math.max(dp[i-1][0]+prices[i]-fee, dp[i-1][1]);
class Solution {public int maxProfit(int[] prices, int fee) {if(prices==null || prices.length==0){return 0;}int len = prices.length;int[][] dp = new int[len][2];dp[0][0] = -prices[0];dp[0][1] = 0;for(int i=1; i<len; i++){dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1]-prices[i]);dp[i][1] = Math.max(dp[i-1][0]+prices[i]-fee, dp[i-1][1]);}return Math.max(dp[len-1][0],dp[len-1][1]);}}
内存优化
class Solution {public int maxProfit(int[] prices, int fee) {if(prices==null || prices.length==0){return 0;}int len = prices.length;int[] dp = new int[2];dp[0] = -prices[0];dp[1] = 0;for(int i=1; i<len; i++){int tmp = dp[0];dp[0] = Math.max(dp[0], dp[1]-prices[i]);dp[1] = Math.max(tmp+prices[i]-fee, dp[1]);}return Math.max(dp[0],dp[1]);}}
123. Best Time to Buy and Sell Stock III (Hard)
题目描述
给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。
注意: 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例
输入: [3,3,5,0,0,3,1,4]
输出: 6
解释: 在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 。
随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3 。
思路
难点在于只能交易两次。
方法一:普通递归
class Solution {public int maxProfit(int[] prices) {if(prices==null || prices.length==0){return 0;}return profit(prices, 0, 0, 0);}public int profit(int[]prices,int index, int way, int count){if(index==prices.length || count==2){return 0;}int x = 0, y=0;x = profit(prices, index+1, way, count); //保持不动//way有两种状态,0不持有,1持有if(way==0){y = profit(prices, index+1, 1, count) - prices[index];}else{y = profit(prices, index+1, 0, count+1) + prices[index];}return Math.max(x,y);}}
方法2:备忘录+递归
class Solution {//定义Keyclass Key{int index;int way;int count;public Key(int index, int way, int count){this.index = index;this.way = way;this.count = count;}//这里需要实现自定义的equals和hashCode函数public int hashCode() {return this.index + this.way + this.count;}public boolean equals(Object obj) {Key other = (Key)obj;if(index==other.index && way==other.way && count==other.count) {return true;}return false;}}public int maxProfit(int[] prices) {if(prices==null || prices.length==0){return 0;}HashMap<Key, Integer> map = new HashMap<>();return profit(prices, map, 0, 0, 0);}public int profit(int[]prices, HashMap<Key, Integer> map, int index, int way, int count){Key key = new Key(index,way,count);if(map.containsKey(key)) {return map.get(key);}if(index==prices.length || count==2){return 0;}int x = 0, y=0;x = profit(prices, map, index+1, way, count); //保持不动//way有两种状态,0不持有,1持有if(way==0){y = profit(prices,map, index+1, 1, count) - prices[index];}else{y = profit(prices, map, index+1, 0, count+1) + prices[index];}map.put(key, Math.max(x,y));return map.get(key);}}
方法3:动态规划
多维书包
dp[x][i][j]:x表示第x天,i表示状态(持有或不持有),j表示个数,dp表示该状态下的最大利润。
class Solution {public int maxProfit(int[] prices) {if(prices==null || prices.length==0){return 0;}int n = prices.length;int[][][] dp = new int[n][2][3];dp[0][0][0] = 0;dp[0][0][1] = 0;dp[0][0][2] = 0;dp[0][1][0] = -prices[0];dp[0][1][1] = -prices[0];dp[0][1][2] = -prices[0]; //注意初始化条件for(int x=1; x<n; x++){dp[x][0][0] = dp[x-1][0][0]; //不持有,卖出个数为0dp[x][0][1] = Math.max(dp[x-1][0][1],dp[x-1][1][0]+prices[x]); //不持有,卖出个数为1;dp[x][0][2] = Math.max(dp[x-1][0][2],dp[x-1][1][1]+prices[x]);dp[x][1][0] = Math.max(dp[x-1][0][0]-prices[x], dp[x-1][1][0]);dp[x][1][1] = Math.max(dp[x-1][0][1]-prices[x], dp[x-1][1][1]);dp[x][1][2] = dp[x-1][1][2];}int a = Math.max(dp[n-1][0][0],dp[n-1][0][1]);int b = Math.max(dp[n-1][1][0],dp[n-1][1][1]);return Math.max(Math.max(a,b),dp[n-1][0][2]);}}
方法三的内存优化
class Solution {public int maxProfit(int[] prices) {if(prices==null || prices.length==0){return 0;}int n = prices.length;int[][] dp = new int[2][3];dp[0][0] = 0;dp[0][1] = 0;dp[0][2] = 0;dp[1][0] = -prices[0];dp[1][1] = -prices[0];dp[1][2] = -prices[0];for(int x=1; x<n; x++){//不持有,卖出个数为0,dp[0][0],dp[1][2]不变dp[0][2] = Math.max(dp[0][2],dp[1][1]+prices[x]);dp[1][1] = Math.max(dp[0][1]-prices[x], dp[1][1]);dp[0][1] = Math.max(dp[0][1],dp[1][0]+prices[x]); //不持有,卖出个数为1;dp[1][0] = Math.max(dp[0][0]-prices[x], dp[1][0]);}int a = Math.max(dp[0][0],dp[0][1]);int b = Math.max(dp[1][0],dp[1][1]);return Math.max(Math.max(a,b),dp[0][2]);}}
方法四:另一种动态规划方法
跟三维数组的有点类似,在三维数组中我们定义了交易次数、买卖状态。 因为交易次数和买卖状态都是常数个,所以我们把这两者整合到一起了。进而将三维数组转化为二维数组。
- dp[i][0] 不持有,0次
- dp[i][1] 持有,0次
- dp[i][2] 不持有,1次
- dp[i][3] 持有,1次
- dp[i][4] 不持有,2次
class Solution {public int maxProfit(int[] prices) {//dp[i][0] 不持有,0次// dp[i][1] 持有,0次// dp[i][2] 不持有,1次// dp[i][3] 持有,1次// dp[i][4] 不持有,2次if(prices==null || prices.length<1){return 0;}int len = prices.length;int[][] dp = new int[len][5];dp[0][0] = 0;dp[0][1] = -prices[0];dp[0][2] = 0;dp[0][3] = -prices[0];dp[0][4] = 0;for(int i=1; i<len; i++){dp[i][0] = dp[i-1][0];dp[i][1] = Math.max(dp[i-1][1], dp[i][0]-prices[i]);dp[i][2] = Math.max(dp[i-1][2], dp[i-1][1]+prices[i]);dp[i][3] = Math.max(dp[i-1][3], dp[i-1][2]-prices[i]);dp[i][4] = Math.max(dp[i-1][4], dp[i-1][3]+prices[i]);}int a = Math.max(dp[len-1][0], dp[len-1][1]);int b = Math.max(dp[len-1][2], dp[len-1][3]);return Math.max(Math.max(a,b),dp[len-1][4]);}}
方法四的内存优化
结果class Solution {public int maxProfit(int[] prices) {//dp[i][0] 不持有,0次// dp[i][1] 持有,0次// dp[i][2] 不持有,1次// dp[i][3] 持有,1次// dp[i][4] 不持有,2次if(prices==null || prices.length<1){return 0;}int len = prices.length;int[] dp = new int[5];dp[0] = 0;dp[1] = -prices[0];dp[2] = 0;dp[3] = -prices[0];dp[4] = 0;for(int i=1; i<len; i++){dp[4] = Math.max(dp[4], dp[3]+prices[i]);dp[3] = Math.max(dp[3], dp[2]-prices[i]);dp[2] = Math.max(dp[2], dp[1]+prices[i]);dp[1] = Math.max(dp[1], dp[0]-prices[i]);}int a = Math.max(dp[0], dp[1]);int b = Math.max(dp[2], dp[3]);return Math.max(Math.max(a,b),dp[4]);}}
188. Best Time to Buy and Sell Stock IV (Hard)
题目描述
给定一个整数数组 prices ,它的第 i 个元素 prices[i] 是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例
输入:k = 2, prices = [3,2,6,5,0,3]
输出:7
解释:在第 2 天 (股票价格 = 2) 的时候买入,在第 3 天 (股票价格 = 6) 的时候卖出, 这笔交易所能获得利润 = 6-2 = 4 。
随后,在第 5 天 (股票价格 = 0) 的时候买入,在第 6 天 (股票价格 = 3) 的时候卖出, 这笔交易所能获得利润 = 3-0 = 3 。
思路
同上题相同,只不过2变为k,分单双数表示持有或不持有。class Solution {public int maxProfit(int k, int[] prices) {if(prices==null || prices.length==0){return 0;}int len = prices.length;//k单数表示持有,双数表示不持有int[][] dp= new int[len][2*k+1];//初始化for(int i=0; i<2*k+1; i++){if(i%2==0){dp[0][i] = 0;}else{dp[0][i] = -prices[0];}}for(int i=1; i<len; i++){dp[i][0] = dp[i-1][0];for(int j=1; j<2*k+1; j++){if(j%2==0){dp[i][j] = Math.max(dp[i-1][j], dp[i-1][j-1]+prices[i]);}else{dp[i][j] = Math.max(dp[i-1][j], dp[i-1][j-1]-prices[i]);}}}int Max = 0;for(int i=1; i<2*k+1; i++){if(dp[len-1][i]>Max){Max = dp[len-1][i];}}return Max;}}
内存优化
结果class Solution {public int maxProfit(int k, int[] prices) {if(prices==null || prices.length==0){return 0;}int len = prices.length;//k单数表示持有,双数表示不持有int[] dp= new int[2*k+1];//初始化for(int i=0; i<2*k+1; i++){if(i%2==0){dp[i] = 0;}else{dp[i] = -prices[0];}}for(int i=1; i<len; i++){for(int j=2*k; j>0; j--){if(j%2==0){dp[j] = Math.max(dp[j], dp[j-1]+prices[i]);}else{dp[j] = Math.max(dp[j], dp[j-1]-prices[i]);}}}int Max = 0;for(int i=1; i<2*k+1; i++){if(dp[i]>Max){Max = dp[i];}}return Max;}}

