【Lintcode】393. Best Time to Buy and Sell Stock IV
題目地址:
https://www.lintcode.com/problem/best-time-to-buy-and-sell-stock-iv/
給定一個股票價格的長 n n n的陣列 p p p,至多可以進行 k k k次買賣,同一天只能進行一次買或者賣,問最多得到的利潤是多少。
首先,如果 k ≥ n / 2 k\ge n/2 k≥n/2,那麼相當於可以進行無限次交易,解法是簡單的。我們考慮 k < n / 2 k< n/2 k<n/2的情形。
思路是狀態機。規定兩個狀態,狀態
1
1
1表示手中有股,
0
0
0表示手中無股。那麼:
1、狀態
1
1
1可以由狀態
1
1
1達到,即昨天手裡有股,然後持有一天到今天仍然有股,則邊權為
0
0
0;狀態
1
1
1可以由狀態
0
0
0達到,即昨天手裡無股,然後今天買入,狀態變為有股,則邊權為
−
p
[
i
]
-p[i]
−p[i];
2、狀態
0
0
0可以由狀態
0
0
0達到,即昨天手裡無股,然後觀望一天,今天仍然無股,則邊權為
0
0
0;狀態
0
0
0可以由狀態
1
1
1達到,即昨天手裡有股,然後今天賣出,狀態變為無股,則邊權為
+
p
[
i
]
+p[i]
+p[i]。
而題目相當於是要問,最多允許從兩個狀態轉
k
k
k圈(表示買入
k
k
k次賣出
k
k
k次)的情況下,最大收益是多少。這裡的動態規劃狀態表示並不唯一,在https://blog.csdn.net/qq_46105170/article/details/109007301這篇文章裡,
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示以有股結尾的,到第
i
i
i天為止的(這裡
i
i
i從
0
0
0計數),已經執行過最多
j
j
j次買入操作的情況下的最大收益,
g
[
i
]
[
j
]
g[i][j]
g[i][j]表示以無股結尾的,到第
i
i
i天為止的(這裡
i
i
i從
0
0
0計數),已經執行過最多
j
j
j次買入操作的情況下的最大收益。我們考慮另一種表示,設
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示以有股結尾的,到第
i
i
i天為止的(這裡
i
i
i從
0
0
0計數),恰好執行過
j
j
j次買入操作的情況下的最大收益,
g
[
i
]
[
j
]
g[i][j]
g[i][j]表示以無股結尾的,到第
i
i
i天為止的(這裡
i
i
i從
0
0
0計數),恰好執行過
j
j
j次買入操作的情況下的最大收益。則有:
f
[
i
]
[
j
]
=
max
{
f
[
i
−
1
]
[
j
]
,
g
[
i
−
1
]
[
j
−
1
]
−
p
[
i
]
}
g
[
i
]
[
j
]
=
max
{
g
[
i
−
1
]
[
j
]
,
f
[
i
−
1
]
[
j
]
+
p
[
i
]
}
f[i][j]=\max\{f[i-1][j],g[i-1][j-1]-p[i]\}\\g[i][j]=\max\{g[i-1][j],f[i-1][j]+p[i]\}
f[i][j]=max{f[i−1][j],g[i−1][j−1]−p[i]}g[i][j]=max{g[i−1][j],f[i−1][j]+p[i]}我們發現這個遞推方程其實是一樣的,不一樣的地方在於最後要返回的答案,應該是
max
j
{
f
[
n
−
1
]
[
j
]
,
g
[
n
−
1
]
[
j
]
}
\max_{j}\{f[n-1][j],g[n-1][j]\}
maxj{f[n−1][j],g[n−1][j]}(因為題目條件是最多進行
k
k
k次交易,只要不超過
k
k
k次交易的答案都是可以的。這裡不能簡單的返回
max
{
f
[
n
−
1
]
[
k
]
,
g
[
n
−
1
]
[
k
]
}
\max\{f[n-1][k],g[n-1][k]\}
max{f[n−1][k],g[n−1][k]},例如對於
p
=
(
3
,
2
,
1
)
p=(3,2,1)
p=(3,2,1),
k
=
1
k=1
k=1,最好的選擇是不買,但如果強制要求買一次,則是虧錢的),並且初始條件也不一樣。我們考慮
f
[
.
]
[
0
]
f[.][0]
f[.][0],
f
[
0
]
[
0
]
f[0][0]
f[0][0]是不存在的,可以賦值為
−
∞
-\infty
−∞以杜絕從這個狀態轉移過來的解,對於
f
[
1
]
[
0
]
f[1][0]
f[1][0],其表示進行一次交易並且以有股結尾,那麼
f
[
1
]
[
0
]
=
−
p
[
0
]
f[1][0]=-p[0]
f[1][0]=−p[0],而對於
i
≥
2
i\ge 2
i≥2,同樣的理由有
f
[
i
]
[
0
]
=
−
∞
f[i][0]=-\infty
f[i][0]=−∞;下面考慮
g
[
.
]
[
0
]
g[.][0]
g[.][0],
g
[
0
]
[
0
]
=
0
g[0][0]=0
g[0][0]=0,而
i
≥
1
i\ge 1
i≥1時,
g
[
i
]
[
0
]
=
−
∞
g[i][0]=-\infty
g[i][0]=−∞,理由同上。程式碼實現方面,需要注意的是,java裡負無窮如果減去一個數,會變成正數。所以程式碼裡的負無窮可以用Integer.MIN_VALUE / 2
來代替。程式碼如下:
public class Solution {
/**
* @param K: An integer
* @param prices: 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) {
int res = 0;
for (int i = 1; i < len; i++) {
res += Math.max(0, prices[i] - prices[i - 1]);
}
return res;
}
// f[i][j]表示已經進行過i次買入,第j天手裡有股
// g[i][j]表示已經進行過i次買入,第j天手裡無股
int[][] f = new int[K + 1][len], g = new int[K + 1][len];
for (int i = 0; i <= K; i++) {
f[i][0] = Integer.MIN_VALUE / 2;
}
f[1][0] = -prices[0];
for (int i = 0; i <= K; i++) {
g[i][0] = Integer.MIN_VALUE / 2;
}
g[0][0] = 0;
int res = 0;
for (int i = 0; i <= K; i++) {
for (int j = 1; j < len; j++) {
f[i][j] = f[i][j - 1];
if (i >= 1) {
f[i][j] = Math.max(f[i][j], g[i - 1][j - 1] - prices[j]);
}
g[i][j] = Math.max(g[i][j - 1], f[i][j - 1] + prices[j]);
// 更新答案
if (j == len - 1) {
res = Math.max(res, f[i][j]);
res = Math.max(res, g[i][j]);
}
}
}
return res;
}
}
時空複雜度 O ( k n ) O(kn) O(kn)。
下面是滾動陣列優化:
public class Solution {
/**
* @param K: An integer
* @param prices: 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) {
int res = 0;
for (int i = 1; i < len; i++) {
res += Math.max(0, prices[i] - prices[i - 1]);
}
return res;
}
// f[i][j]表示已經進行過i次買入,第j天手裡有股
// g[i][j]表示已經進行過i次買入,第j天手裡無股
int[][] f = new int[2][len], g = new int[2][len];
f[0][0] = Integer.MIN_VALUE / 2;
f[1][0] = -prices[0];
g[0][0] = 0;
g[1][0] = Integer.MIN_VALUE / 2;
int res = 0;
for (int i = 0; i <= K; i++) {
for (int j = 1; j < len; j++) {
f[i & 1][j] = f[i & 1][j - 1];
if (i >= 1) {
f[i & 1][j] = Math.max(f[i & 1][j], g[i - 1 & 1][j - 1] - prices[j]);
}
g[i & 1][j] = Math.max(g[i & 1][j - 1], f[i & 1][j - 1] + prices[j]);
if (j == len - 1) {
res = Math.max(res, f[i & 1][j]);
res = Math.max(res, g[i & 1][j]);
}
}
}
return res;
}
}
時間複雜度不變,空間 O ( n ) O(n) O(n)。
相關文章
- Best Time to Buy and Sell Stock系列
- 121|Best Time to Buy and Sell Stock
- [leetcode]Best Time to Buy and Sell StockLeetCode
- 121. Best Time to Buy and Sell Stock
- Best Time to Buy and Sell Stock系列分析
- 貪心法-Best Time to Buy and Sell Stock
- [LeetCode] 121. Best Time to Buy and Sell StockLeetCode
- leetcode_best-time-to-buy-and-sell-stock-iiLeetCode
- LeetCode 309. Best Time to Buy and Sell Stock with CooldownLeetCode
- leetcode best-time-to-buy-and-sell-stock-iii(Java)LeetCodeJava
- [LeetCode] 122. Best Time to Buy and Sell Stock IILeetCode
- 【leetcode】40-best-time-to-buy-and-sell-stock 力扣 121. 買賣股票的最佳時機LeetCode力扣
- 42-best-time-to-buy-and-sell-stock-iii 力扣 123. 買賣股票的最佳時機 III力扣
- 【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. 買賣股票的最佳時機包含冷凍期力扣
- Time Series Analysis (Best MSE Predictor & Best Linear Predictor)
- [LeetCode] 2073. Time Needed to Buy TicketsLeetCode
- 【PAT_1062】To Buy or Not to Buy
- TTEC遭勒索軟體攻擊後影響客戶業務 包括Verizon、Best Buy、美國銀行等
- SAP MM GR-based IV, 無GR不能IV?
- SAP Stock Inconsistency
- Java加密之IVJava加密
- The best LeetCode NodesLeetCode
- Best Team With No Conflicts
- IV. Mip-NeRF
- SELL 指令碼程式設計指令碼程式設計
- Best Wishes「兔」You!
- [LeetCode] 2326. Spiral Matrix IVLeetCode
- [LintCode] Daily TemperaturesAI
- [LintCode] Permutation in String
- 攻防世界-best_rsa
- 25 Best Java Books In 2022Java
- 題解:P10688 Buy Tickets
- 653-Two Sum IV - Input is a BST
- SAP HUM 巢狀HU初探 IV巢狀
- 力扣 653. 兩數之和 IV 二叉樹/binary-tree two-sum IV力扣二叉樹
- time time_t tm用法
- [LintCode/LeetCode] Meeting RoomsLeetCodeOOM