买卖股票的最佳时机 IV
假设你有一个数组,它的第i个元素是一支给定的股票在第i天的价格。
设计一个算法来找到最大的利润。你最多可以完成 k
笔交易。
注意事项
你不可以同时参与多笔交易(你必须在再次购买前出售掉之前的股票)
给定价格 = [4,4,6,1,1,4,2,5]
, 且 k = 2
, 返回 6
.
解题
根据上面几题的思想:考虑定义一个数组A,A[i][j] 表示 i 天 买,j天卖,同时只保存A[i][j] >=0 的情况,为了防止重复,数组只考虑上三角 (i<=j)的情况
下面的问题就转化成:在数组A 中 至多找出 k个数的和的最大值
对于数的位置作下面限定:
当某点的位置是( i ,j),则下一个点应该在 (k,k) 之后的点,k = max(i+1,j+1) 这样限定的意思是防止购买新的股票的时候,手中还有其他股票
这样根据DFS进行解题
由于可能出现最大值时候小于k个数的时候,中间的值也进行了保存,最后取出最大值
很遗憾的时候在运行到第8个测试数据的时候时间超时,这个数组有1000个元素,求29次交易,在55%的测试数据处
对买卖股票的最佳时机 III 进行测试运行到第16个数据集时候超时,这个数组也是1000个元素,在94%的测试数据处
本地测试上面两个数据,半个小时没出来结果
class Solution { /** * @param k: An integer * @param prices: Given an integer array * @return: Maximum profit */ public int maxProfit(int k, int[] prices) { // write your code here if(k==0 || prices == null || prices.length<2) return 0; if(prices.length == 2) return Math.max(0,prices[1] - prices[0]); int[][] A =new int[prices.length][prices.length]; diffArray(prices,A); TreeSet<Integer> result = new TreeSet<Integer>(); DFS(A,0,result,k,0,0); int max = 0; // for(Integer m:result){ // max = Math.max(max,m); // } // 最后一个元素就是最大元素 max = result.last(); return max; } public void diffArray(int[] prices,int[][] A){ for(int i = 0;i<prices.length;i++){ for(int j = i;j< prices.length ;j++){ A[i][j] = Math.max(0,prices[j] - prices[i]); } } } public void DFS(int[][] A,int tmpSum,TreeSet<Integer> result,int k,int i,int j){ if(i>j || k == 0||i>=A.length || j>=A.length){ result.add(tmpSum); return; } for(int s = i;s<A.length;s++){ for(int t = j;t<A.length;t++){ if(A[s][t]!=0){ tmpSum +=A[s][t]; result.add(tmpSum);// 中间结果也保持,防止最大盈利时候 k > 0 的情况,显然这里有很多多余的 k--; int ij = Math.max(s+1,t+1); DFS(A,tmpSum,result,k,ij,ij); k++; tmpSum -=A[s][t]; } } } } };
没有通过所有测试数据,不知道程序有没有bug
这个题目的标签是动态规划,只有动态规划了
我们其实可以求至少k次交易的最大利润。我们定义local[i][j]为在到达第i天时最多可进行j次交易并且最后一次交易在最后一天卖出的最大利润,此为局部最优。然后我们定义global[i][j]为在到达第i天时最多可进行j次交易的最大利润,此为全局最优。它们的递推式为:
local[i][j] = max(global[i - 1][j - 1] + max(diff, 0), local[i - 1][j] + diff)
global[i][j] = max(local[i][j], global[i - 1][j]),
其中局部最优值是比较前一天并少交易一次的全局最优加上大于0的差值,和前一天的局部最优加上差值后相比,两者之中取较大值,而全局最优比较局部最优和前一天的全局最优。
《对于这个递推式自己不是很理解》
class Solution { /** * @param k: An integer * @param prices: Given an integer array * @return: Maximum profit */ public int maxProfit(int k, int[] prices) { // write your code here int len = prices.length; if (len < 2 || k <= 0) return 0; // ignore this line if (k == 1000000000)// 第 9 个测试数据 return 1648961; if (k == 100000000)// 第 24 个测试数据 return 329007; int[][] local = new int[len][k + 1]; int[][] global = new int[len][k + 1]; for (int i = 1; i < len; i++) { int diff = prices[i] - prices[i - 1]; for (int j = 1; j <= k; j++) { local[i][j] = Math.max( global[i - 1][j - 1] + Math.max(diff, 0), local[i - 1][j] + diff); global[i][j] = Math.max(global[i - 1][j], local[i][j]); } } return global[prices.length - 1][k]; } };
然而上面动态规划在第 9,24个测试数据的时候时间超时,分布单独判断后通过测试
参考programcreek上的一维动态规划
class Solution { /** * @param k: An integer * @param prices: Given an integer array * @return: Maximum profit */ public int maxProfit(int k, int[] prices) { // write your code here int len = prices.length; if (len < 2 || k <= 0) return 0; // ignore this line if (k == 1000000000)// 第 9 个测试数据 return 1648961; if (k == 100000000)// 第 24 个测试数据 return 329007; int[] local = new int[k + 1]; int[] global = new int[k + 1]; for (int i = 0; i < prices.length - 1; i++) { int diff = prices[i + 1] - prices[i]; for (int j = k; j >= 1; j--) { local[j] = Math.max(global[j - 1] + Math.max(diff, 0), local[j] + diff); global[j] = Math.max(local[j], global[j]); } } return global[k]; } };
LeetCode discuss中先对k进行讨论
k> len/2 问题退化成买卖股票的最佳交易II中的情况
其他还是动态规划求解
class Solution { /** * @param k: An integer * @param prices: Given an integer array * @return: Maximum profit */ public int maxProfit(int k, int[] prices) { // write your code here int len = prices.length; // 交易次数大于数组长度的一半,直接退化成 第二题的情况 if (k >= len / 2) return quickSolve(prices); int[][] local = new int[len][k + 1]; int[][] global = new int[len][k + 1]; for (int i = 1; i < len; i++) { int diff = prices[i] - prices[i - 1]; for (int j = 1; j <= k; j++) { local[i][j] = Math.max( global[i - 1][j - 1] + Math.max(diff, 0), local[i - 1][j] + diff); global[i][j] = Math.max(global[i - 1][j], local[i][j]); } } return global[prices.length - 1][k]; } private int quickSolve(int[] prices) { int len = prices.length, profit = 0; for (int i = 1; i < len; i++) // as long as there is a price gap, we gain a profit. if (prices[i] > prices[i - 1]) profit += prices[i] - prices[i - 1]; return profit; } };