[Leetcode188] 买卖股票的最佳时机IV 动态规划 解题报告

题源:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iv/

本题代码:

 1 /**
 2  * @author yuan
 3  * @version 0.1
 4  * @date 2019/4/5
 5  */
 6 public class Leetcode188 {
 7
 8     private int getBetterBuy(int[] buy, int[] sell, int price, int i) {
 9         if (i == 0) {
10             return Math.max(buy[i],-price);
11         } else {
12             return Math.max(buy[i], sell[i - 1] - price);
13         }
14     }
15
16     private int getBetterSell(int[] buy, int[] sell, int price, int i) {
17         return Math.max(sell[i], buy[i] + price);
18     }
19
20     /** @see Leetcode122 */
21     public int greedy(int[] prices) {
22         int result = 0;
23         // 当前是否持有股票
24         boolean has = false;
25         // 当前所持有股票的购入价
26         int buyInPrice = 0;
27         for (int i = 0; i < prices.length; i++) {
28             if (!has) {
29                 if (i + 1 < prices.length && prices[i + 1] > prices[i]) {
30                     has = true;
31                     buyInPrice = prices[i];
32                 }
33             } else if (i + 1 == prices.length || prices[i + 1] < prices[i]) {
34                 has = false;
35                 result += prices[i] - buyInPrice;
36             }
37         }
38         return result;
39     }
40
41     /** @see Leetcode123 */
42     public int maxProfit(int k, int[] prices) {
43         if (k <= 0 || prices == null || prices.length == 0) {
44             return 0;
45         }
46         // k足够大的时候退化为贪心
47         if (k >= prices.length / 2) {
48             return greedy(prices);
49         }
50         // 第i次买的收益
51         int[] buy = new int[k];
52         for (int i = 0; i < k; i++) {
53             buy[i] = Integer.MIN_VALUE;
54         }
55         // 第i次卖的收益
56         int[] sell = new int[k];
57         for (int price : prices) {
58             for (int j = 0; j < k; j++) {
59                 buy[j] = getBetterBuy(buy, sell, price, j);
60                 sell[j] = getBetterSell(buy, sell, price, j);
61             }
62         }
63         return sell[k - 1];
64     }
65 }

整体思路:

  要进行n次交易,至少需要2n天,如果k大于或等于数组长度的一半,那么这题就退化为买卖股票的最佳时机 II,采用贪心法来做(见Leetcode上的题解)。

  如果k小于数组长度的一半,那么就变成了买卖股票的最佳时机 III的扩展,买卖股票的最佳时机 III动态规划解法如下:

  

  原出处:https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/discuss/39611/Is-it-Best-Solution-with-O(n)-O(1).

  将这4个变量扩展为2k个变量,用数组保存,以同样方式处理,之后返回最后一次出售的收益即可。

  本题动态规划的具体细节与买卖股票的最佳时机 III相同。

本题状态转移方程:

  but[t][i] = max{ buy[t-1][i], sell[t-1][i-1] - prices[t] }

  sell[t][i] = max{ sell[t-1][i], buy[t-1][i] + prices[t] }

  其中t表示天数,i表示交易次数。具体说明见下文。

  t这个维度可以用滚动数组省去。

算法细节:

  将买卖股票的最佳时机 III的动态规划结合买卖股票的最佳时机 II的贪心写出这题的代码不难,如果你已经明白买卖股票的最佳时机 III的动态规划算法是怎么回事,那么下面的这一堆字就没必要看了??。下面是对上文本题代码的解释。

  首先要明确buy[i]和sell[i]含义。用t表示考虑前t天的情况(从0开始),并将在最晚第t天进行至多第i次购入的收益表示为buy[t][i],在最晚第t天进行至多第i次出售的收益表示为sell[t][i]。显然购入操作的将带来负收益。题目给的k是指最多进行k次交易,而最优解的交易次数可能是小于k的,因此buy[t][i]和sell[t][i]的含义并不是第i次购入或出售后的收益,而是指在该天进行第[小于或等于i]次购入或出售后的收益。比如k=2,prices={2,4,1,0,0,0,0,0}的情况,显然第1天购入第2天出售可以获得最大收益,最终结果sell[t=7][i=1]应与sell[t=1][i=0]的值相同。也就是说,如果最优解一共进行了x次交易,且这次交易的出售在第y天进行,那么对所有i>=x,t>=y,buy[t][i]与sell[t][i]必须分别等于buy[y][x]和sell[y][x],最终结果才能用sell[prices.length-1][k-1]表示。采用滚动数组可以省去天数这个维度,但下文部分地方为方便说明,仍会把天数标出。

  明确buy[]和sell[]的含义后,状态转移方程本身是否正确比较好理解,这里不进行解释。

  第i次出售必须在第i次购入之后进行,并且每天只能进行一次操作,那么每天都计算整个buy[]和sell[]数组不会出错吗?并不会。

  假设一天可以进行多次操作,如果同一天各一次买入和卖出,这两次操作是不会有收益的,所以在第一天计算第一次出售的收益会是0。既然在第一天进行第一次出售的收益是0,那么第一天进行第二次购入的收益就将和第一次购入的收益相同,如果闲得蛋疼,买一次卖一次再买一次再卖一次,最后再买第三次,产生的收益也和第一次购入相同。如果第一天进行了第一次购入,第二天进行第一次售出后再进行第二次购入,那么就相当于这一天什么也没做,总收益仍然等于第一天的第一次购入产生的收益。依此类推不难发现,如果在每天只能操作依次时当前这一天至多只能进行第x次购入,那么取消限制后,对于所有i>x,buy[i]是等于buy[x]的,并且sell[x]等于sell[x-1],接着可以推出所有这些sell[i]都等于sell[x-1]。因此,对于每个prices[i],即每一天,都对整个buy[]和sell[]数组进行计算,可以使得sell[k-1]等于最优解的收益。于是sell[k-1]就是我们要的结果。

  以k=2,prices={2,4,1,1,0}为例说明:

  首先初始化buy[]为全-∞(Java中用Integer.MIN_VALUE模拟),sell[]为全0。

  在第1天(t=0),buy[0]显然会等于-price[0],即-2。

  此时虽然还不能产生第1次出售,但sell[0]=max{sell[0],buy[0]+prices[0]},结果将会是0。

  此时虽然还不能产生第2次购入,但buy[1]=max{buy[1],sell[0]-prices[0]},将和buy[0]相等。

  此时虽然还不能产生第2次出售,但sell[1]=max{sell[1],buy[1]+prices[0]},结果也会是0。实际上既然buy[1]和buy[0]相等,sell[1]也就等于sell[0]。

  于是,如果只考虑第1天,最大收益sell[1]是0,结果正确。

  再考虑第2天(t=1)。

  buy[0]=max{buy[0],sell[0]-prices[1]},结果仍然是-2。

  sell[0]=max{sell[1],buy[0]+prices[1]},结果为2。

  此时虽然还不能产生第2次购入,但由于buy[0][1]与buy[0][0]相等,且buy[1][1]=max{buy[0][1],sell[1][0]-prices[1]},相当于buy[1][1]=max{buy[0][0],sell[0][0]-prices[1]}。最终buy[1][1]和buy[1][0]相等,即第二天的buy[1]仍然和buy[0]相等,值为-2。

  此时虽然还不能产生第2次出售,但sell[1]=max{sell[1],buy[1]+prices[1]},结果也是2。

  在只考虑前2天的情况下,最大收益sell[1]是2,结果正确。

  再考虑第3天。

  buy[0]=max{buy[0],-prices[2]},变为-1。

  sell[0]=max{sell[0],buy[0]+prices[2]},仍为2。

  buy[1=max{buy[1],sell[0]-prices[2]},变为1。

  此时虽然还不能产生第2次出售,sell[1]=max{sell[1],buy[1]+prices[2]},结果仍为2。

  于是只考虑前3天的情况下,最大收益sell[1]为2,结果正确。

  再考虑第4天。

  buy[0]=max{buy[0],-prices[3]},仍为-1。

  sell[0]=max{sell[0],buy[0]+prices[3]},仍为2。

  buy[1]=max{buy[1],sell[0]+prices[3]},仍为1。

  sell[1]=max{sell[1],buy[1]+prices[3]},仍为2。

  于是只考虑前4天的情况下,最大收益sell[1]为2,结果正确。

  最后考虑第5天

  buy[0]=max{buy[0],-prices[4]},变为0。

  sell[0]=max{sell[0],buy[0]+prices[4]},仍为2。

  buy[1]=max{buy[1],sell[0]+prices[4]},变为2。

  sell[1]=max{sell[1],buy[1]+prices[4]},仍为2。

  最终最大收益sell[1]为2,结果正确。

附另外两题的代码

 1 /**
 2  * @author yuan
 3  * @version 0.1
 4  * @date 2019/4/5
 5  */
 6 public class Leetcode122 {
 7     public int maxProfit(int[] prices) {
 8         int result = 0;
 9         int buyInPrice = 0;
10         boolean has = false;
11         for (int i = 0; i < prices.length; i++) {
12             if (!has) {
13                 if (i + 1 < prices.length && prices[i + 1] > prices[i]) {
14                     has = true;
15                     buyInPrice = prices[i];
16                 }
17             } else if (i + 1 == prices.length || prices[i + 1] < prices[i]) {
18                 has = false;
19                 result += prices[i] - buyInPrice;
20             }
21         }
22         return result;
23     }
24 }
 1 /**
 2  * @author yuan
 3  * @version 0.1
 4  * @date 2019/4/5
 5  */
 6 public class Leetcode123 {
 7     public int maxProfit(int[] prices) {
 8         /*
 9          对于任意一天考虑四个变量:
10          firstBuy: 在该天第一次买入股票可获得的最大收益
11          firstSell: 在该天第一次卖出股票可获得的最大收益
12          secondBuy: 在该天第二次买入股票可获得的最大收益
13          secondSell: 在该天第二次卖出股票可获得的最大收益
14          分别对四个变量进行相应的更新, 最后secSell就是最大
15          收益值(secondSell >= firstBuy)
16          */
17         int firstBuy = Integer.MIN_VALUE;
18         int firstSell = 0;
19         int secondBuy = Integer.MIN_VALUE;
20         int secondSell = 0;
21         for (int price : prices) {
22             firstBuy = Math.max(firstBuy, -price);
23             // 如果在第一天,这个结果是0
24             firstSell = Math.max(firstSell, firstBuy + price);
25             // 如果在第一天,将等于firstBuy;如果在第二天,由于第一天等于firstBuy,仍将等于firstBuy
26             secondBuy = Math.max(secondBuy, firstSell - price);
27             // 如果在前两天,由于secondBuy等于firstBuy,结果等于firstSell;如果在第三天,secondBuy + price等于firstSell
28             secondSell = Math.max(secondSell, secondBuy + price);
29         }
30         return secondSell;
31     }
32 }

原文地址:https://www.cnblogs.com/null-0/p/10659432.html

时间: 2024-07-31 04:58:51

[Leetcode188] 买卖股票的最佳时机IV 动态规划 解题报告的相关文章

删除排序数组中的重复数字、买卖股票的最佳时机、爬楼梯

题目1:删除排序数组中的重复数字 描述:给定一个排序数组,在原数组中删除重复出现的数字,使得每个元素只出现一次,并且返回新的数组的长度. 不要使用额外的数组空间,必须在原地没有额外空间的条件下完成. 题目2:买卖股票的最佳时机 描述:假设有一个数组,它的第i个元素是一支给定的股票在第i天的价格.如果你最多只允许完成一次交易(例如,一次买卖股票),设计一个算法来找出最大利润. 题目3:爬楼梯 描述:假设你正在爬楼梯,需要n步你才能到达顶部.但每次你只能爬一步或者两步,你能有多少种不同的方法爬到楼顶

代码题(6)— 买卖股票的最佳时机

1.121. 买卖股票的最佳时机(仅限一次) 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格. 如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润. 注意你不能在买入股票前卖出股票. 示例 1: 输入: [7,1,5,3,6,4] 输出: 5 解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 . 注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格. c

Leetcode 123.买卖股票的最佳时机III

买卖股票的最佳时机III 给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格. 设计一个算法来计算你所能获取的最大利润.你最多可以完成 两笔 交易. 注意: 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票). 示例 1: 输入: [3,3,5,0,0,3,1,4] 输出: 6 解释: 在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 . 随后,在第 7 天(股票价格 = 1)的时候买入,在

python买卖股票的最佳时机--贪心/蛮力算法简介

开始刷leetcode算法题 今天做的是"买卖股票的最佳时机" 题目要求 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格. 设计一个算法来计算你所能获取的最大利润.你可以尽可能地完成更多的交易(多次买卖一支股票). 注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票). 看到这个题目 最初的想法是蛮力法 通过两层循环 不断计算不同天之间的利润及利润和 下面上代码 1 class Solution(object): 2 def maxProfit(self,

Leecode刷题之旅-C语言/python-121买卖股票的最佳时机

/* * @lc app=leetcode.cn id=121 lang=c * * [121] 买卖股票的最佳时机 * * https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/description/ * * algorithms * Easy (48.50%) * Total Accepted: 32.5K * Total Submissions: 66.9K * Testcase Example: '[7,1,5

【Leetcode】【简单】【122. 买卖股票的最佳时机 II】【JavaScript】

题目描述 122. 买卖股票的最佳时机 II 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格. 设计一个算法来计算你所能获取的最大利润.你可以尽可能地完成更多的交易(多次买卖一支股票). 注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票). 示例 1: 输入: [7,1,5,3,6,4]输出: 7解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 .随后,在第 4 天(股票价格

每日一题-——LeetCode(121)买卖股票的最佳时机

题目描述: 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格.如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润.注意你不能在买入股票前卖出股票. 示例 输入: [7,1,5,3,6,4] 输出:5 定义:minval = min(minval,prices[i]) 当前的最小价格 maxp = max(maxp,prices[i]-minval) 当前的最大利润 class Solution(object): def maxProfit(

LeetCode | 0121. Best Time to Buy and Sell Stock买卖股票的最佳时机【Python】

LeetCode 0121. Best Time to Buy and Sell Stock买卖股票的最佳时机[Easy][Python][贪心] Problem LeetCode Say you have an array for which the ith element is the price of a given stock on day i. If you were only permitted to complete at most one transaction (i.e., b

买卖股票的最佳时机I II III IV

I 假设有一个数组,它的第i个元素是一支给定的股票在第i天的价格.如果你最多只允许完成一次交易(例如,一次买卖股票),设计一个算法来找出最大利润. II 假设有一个数组,它的第i个元素是一个给定的股票在第i天的价格.设计一个算法来找到最大的利润.你可以完成尽可能多的交易(多次买卖股票).然而,你不能同时参与多个交易(你必须在再次购买前出售股票). III 假设你有一个数组,它的第i个元素是一支给定的股票在第i天的价格.设计一个算法来找到最大的利润.你最多可以完成两笔交易. 样例 给出一个样例数组