【LeetCode】 Best Time to Buy and Sell Stock I II III IV 解題報告
原文地址:http://liangjiabin.com/blog/2015/04/leetcode-best-time-to-buy-and-sell-stock.html (已失效)
Best Time to Buy and Sell Stock I
題意: 用一個陣列表示股票每天的價格,陣列的第i個數表示股票在第i天的價格。 如果只允許進行一次交易,也就是說只允許買一支股票並賣掉,求最大的收益。
分析: 動態規劃法。從前向後遍歷陣列,記錄當前出現過的最低價格,作為買入價格,並計算以當天價格出售的收益,作為可能的最大收益,整個遍歷過程中,出現過的最大收益就是所求。
程式碼: 時間O(n),空間O(1)。
public class Solution {
public int maxProfit(int[] prices) {
if (prices.length < 2) return 0;
int maxProfit = 0;
int curMin = prices[0];
for (int i = 1; i < prices.length; i++) {
curMin = Math.min(curMin, prices[i]);
maxProfit = Math.max(maxProfit, prices[i] - curMin);
}
return maxProfit;
}
}
Best Time to Buy and Sell Stock II
題目: 用一個陣列表示股票每天的價格,陣列的第i個數表示股票在第i天的價格。交易次數不限,但一次只能交易一支股票,也就是說手上最多隻能持有一支股票,求最大收益。
分析: 貪心法。從前向後遍歷陣列,只要當天的價格高於前一天的價格,就算入收益。
程式碼: 時間O(n),空間O(1)。
public class Solution {
public int maxProfit(int[] prices) {
if (prices.length < 2) return 0;
int maxProfit = 0;
for (int i = 1; i < prices.length; i++) {
int diff = prices[i] - prices[i - 1];
if (diff > 0) {
maxProfit += diff;
}
}
return maxProfit;
}
}
Best Time to Buy and Sell Stock III
題意: 用一個陣列表示股票每天的價格,陣列的第i個數表示股票在第i天的價格。最多交易兩次,手上最多隻能持有一支股票,求最大收益。
分析: 動態規劃法。以第i天為分界線,計算第i天之前進行一次交易的最大收益preProfit[i],和第i天之後進行一次交易的最大收益postProfit[i],最後遍歷一遍,max{preProfit[i] + postProfit[i]} (0≤i≤n-1)就是最大收益。第i天之前和第i天之後進行一次的最大收益求法同Best Time to Buy and Sell Stock I。
程式碼: 時間O(n),空間O(n)。
public class Solution {
public int maxProfit(int[] prices) {
if (prices.length < 2) return 0;
int n = prices.length;
int[] preProfit = new int[n];
int[] postProfit = new int[n];
int curMin = prices[0];
for (int i = 1; i < n; i++) {
curMin = Math.min(curMin, prices[i]);
preProfit[i] = Math.max(preProfit[i - 1], prices[i] - curMin);
}
int curMax = prices[n - 1];
for (int i = n - 2; i >= 0; i--) {
curMax = Math.max(curMax, prices[i]);
postProfit[i] = Math.max(postProfit[i + 1], curMax - prices[i]);
}
int maxProfit = 0;
for (int i = 0; i < n; i++) {
maxProfit = Math.max(maxProfit, preProfit[i] + postProfit[i]);
}
return maxProfit;
}
}
Best Time to Buy and Sell Stock IV
題意: 用一個陣列表示股票每天的價格,陣列的第i個數表示股票在第i天的價格。最多交易k次,手上最多隻能持有一支股票,求最大收益。
分析: 特殊動態規劃法。傳統的動態規劃我們會這樣想,到第i天時進行j次交易的最大收益,要麼等於到第i-1天時進行j次交易的最大收益(第i天價格低於第i-1天的價格),要麼等於到第i-1天時進行j-1次交易,然後第i天進行一次交易(第i天價格高於第i-1天價格時)。於是得到動規方程如下(其中diff = prices[i] – prices[i – 1]):
profit[i][j] = max(profit[i – 1][j], profit[i – 1][j – 1] + diff)
看起來很有道理,但其實不對,為什麼不對呢?因為diff是第i天和第i-1天的差額收益,如果第i-1天當天本身也有交易呢,那麼這兩次交易就可以合為一次交易,這樣profit[i – 1][j – 1] + diff實際上只進行了j-1次交易,而不是最多可以的j次,這樣得到的最大收益就小了。
那麼怎樣計算第i天進行交易的情況的最大收益,才會避免少計算一次交易呢?我們用一個區域性最優解和全域性最有解表示到第i天進行j次的收益,這就是該動態規劃的特殊之處。
用local[i][j]表示到達第i天時,最多進行j次交易的區域性最優解;用global[i][j]表示到達第i天時,最多進行j次的全域性最優解。它們二者的關係如下(其中diff = prices[i] – prices[i – 1]):
local[i][j] = max(global[i – 1][j – 1] + max(diff, 0), local[i – 1][j] + diff)
global[i][j] = max(global[i – 1][j], local[i][j])
其中的local[i – 1][j] + diff就是為了避免第i天交易和第i-1天交易合併成一次交易而少一次交易收益。 參考:http://www.cnblogs.com/grandyang/p/4295761.html
程式碼: 時間O(n),空間O(k)。
public class Solution {
public int maxProfit(int k, int[] prices) {
if (prices.length < 2) return 0;
int days = prices.length;
if (k >= days) return maxProfit2(prices);
int[][] local = new int[days][k + 1];
int[][] global = new int[days][k + 1];
for (int i = 1; i < days ; 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], local[i - 1][j] + diff);
global[i][j] = Math.max(global[i - 1][j], local[i][j]);
}
}
return global[days - 1][k];
}
public int maxProfit2(int[] prices) {
int maxProfit = 0;
for (int i = 1; i < prices.length; i++) {
if (prices[i] > prices[i - 1]) {
maxProfit += prices[i] - prices[i - 1];
}
}
return maxProfit;
}
}
我們知道,動規所用的二維輔助陣列可以降為一維的,即只用大小為k的一維陣列記錄到達第i天時的區域性最優解和全域性最優解。需要注意的是,由於第i天時交易k次的最優解依賴於第i-1天時交易k-1次的最優解,所以陣列更新應當從後往前(即從k到1)更新。
程式碼: 時間O(nk),空間O(k)。
public class Solution {
public int maxProfit(int k, int[] prices) {
if (prices.length < 2) return 0;
if (k >= prices.length) return maxProfit2(prices);
int[] local = new int[k + 1];
int[] global = new int[k + 1];
for (int i = 1; i < prices.length ; i++) {
int diff = prices[i] - prices[i - 1];
for (int j = k; j > 0; j--) {
local[j] = Math.max(global[j - 1], local[j] + diff);
global[j] = Math.max(global[j], local[j]);
}
}
return global[k];
}
public int maxProfit2(int[] prices) {
int maxProfit = 0;
for (int i = 1; i < prices.length; i++) {
if (prices[i] > prices[i - 1]) {
maxProfit += prices[i] - prices[i - 1];
}
}
return maxProfit;
}
}
補充: 這道題還有一個陷阱,就是當k大於天數時,其實就退化成 Best Time to Buy and Sell Stock II 了。就不能用動規來做了,為什麼?(請思考) 另外,Best Time to Buy and Sell Stock III 就是本題k=2的情況,所以說IV是II和III的綜合。
(完)
相關文章
- leetcode_best-time-to-buy-and-sell-stock-iiLeetCode
- leetcode best-time-to-buy-and-sell-stock-iii(Java)LeetCodeJava
- [LeetCode] 122. Best Time to Buy and Sell Stock IILeetCode
- [leetcode]Best Time to Buy and Sell StockLeetCode
- 【Lintcode】393. Best Time to Buy and Sell Stock IV
- [LeetCode] 121. Best Time to Buy and Sell StockLeetCode
- LeetCode 309. Best Time to Buy and Sell Stock with CooldownLeetCode
- Best Time to Buy and Sell Stock系列
- 121|Best Time to Buy and Sell Stock
- 121. Best Time to Buy and Sell Stock
- Best Time to Buy and Sell Stock系列分析
- 貪心法-Best Time to Buy and Sell Stock
- 42-best-time-to-buy-and-sell-stock-iii 力扣 123. 買賣股票的最佳時機 III力扣
- 【leetcode】40-best-time-to-buy-and-sell-stock 力扣 121. 買賣股票的最佳時機LeetCode力扣
- 【LeetCode】309. Best Time to Buy and Sell Stock with Cooldown 最佳買賣股票時機含冷凍期(Medium)(JAVA)LeetCodeJava
- 44-best-time-to-buy-and-sell-stock-with-cooldown 力扣 309. 買賣股票的最佳時機包含冷凍期力扣
- [LeetCode] 2073. Time Needed to Buy TicketsLeetCode
- 【LeetCode】253. Meeting Rooms II 解題報告(C++)LeetCodeOOMC++
- 【LeetCode動態規劃#12】詳解買賣股票I~IV,經典dp題型LeetCode動態規劃
- LeetCode題解(0407):接雨水II(Python)LeetCodePython
- Leetcode Weekly Contest 95解題報告LeetCode
- LeetCode Weekly Contest 96 解題報告LeetCode
- Time Series Analysis (Best MSE Predictor & Best Linear Predictor)
- [Leetcode學習-c++&java]Next Greater Element I ~ IIILeetCodeC++Java
- LeetCode題解(0210):課程表II(Python)LeetCodePython
- Leetcode Weekly Contest94 解題報告LeetCode
- The best LeetCode NodesLeetCode
- LeetCode 272 Closest Binary Tree Traversal II 解題思路LeetCode
- Leetcode 第136場周賽解題報告LeetCode
- 題解:P10688 Buy Tickets
- [LeetCode] 2326. Spiral Matrix IVLeetCode
- 【LeetCode回溯演算法#07】子集問題I+II,鞏固解題模板並詳解回溯演算法中的去重問題LeetCode演算法
- 【糊題】CCPC2023桂林站 I題 Barkley II
- [LintCode/LeetCode] Contains Duplicate IIILeetCodeAI
- 【leetcode】每日精選題詳解之59. 螺旋矩陣 IILeetCode矩陣
- LeetCode 285 & 510. 二叉樹中序後繼 I & IILeetCode二叉樹
- Goal2: buy 85 mining, I now have 74 mining and I believeGo
- 【LeetCode】416. Partition Equal Subset Sum 解題報告(Python & C++)LeetCodePythonC++
- [LeetCode] 248. Strobogrammatic Number IIILeetCode