Skip to content

Commit f236073

Browse files
committed
sovle #188
1 parent 2de7d7c commit f236073

File tree

2 files changed

+99
-0
lines changed

2 files changed

+99
-0
lines changed

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,3 +163,4 @@ mod n0173_binary_search_tree_iterator;
163163
mod n0174_dungeon_game;
164164
mod n0179_largest_number;
165165
mod n0187_repeated_dna_sequences;
166+
mod n0188_best_time_to_buy_and_sell_stock_iv;
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/**
2+
* [188] Best Time to Buy and Sell Stock IV
3+
*
4+
* Say you have an array for which the i^th element is the price of a given stock on day i.
5+
*
6+
* Design an algorithm to find the maximum profit. You may complete at most k transactions.
7+
*
8+
* Note:<br />
9+
* You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).
10+
*
11+
* Example 1:
12+
*
13+
*
14+
* Input: [2,4,1], k = 2
15+
* Output: 2
16+
* Explanation: Buy on day 1 (price = 2) and sell on day 2 (price = 4), profit = 4-2 = 2.
17+
*
18+
*
19+
* Example 2:
20+
*
21+
*
22+
* Input: [3,2,6,5,0,3], k = 2
23+
* Output: 7
24+
* Explanation: Buy on day 2 (price = 2) and sell on day 3 (price = 6), profit = 6-2 = 4.
25+
* Then buy on day 5 (price = 0) and sell on day 6 (price = 3), profit = 3-0 = 3.
26+
*
27+
*/
28+
pub struct Solution {}
29+
30+
// submission codes start here
31+
32+
/*
33+
已经在 #123 里解过了, 为了方便阅读直接把那题的分析拷贝到这里
34+
35+
先考虑只进行 1 次交易的情况, 我们求以 i *为售出点*, 只进行 1 次交易获得的最大利润, 那么:
36+
37+
f[i] = if f[i-1] > 0 { f[i-1] } else { 0 } + prices[i] - prices[i-1]
38+
39+
这很容易解, 解完之后找出 f 里的最大值即可, 但这不容易推广到 K 次交易的情况, 因为这时 f[i] 不代表到 i *为止*的最大利润, 无法作为单独的交易帮助递推
40+
(到 i 为止的含义是售出点可以在 [0,i] 之间)
41+
42+
我们可以稍作改进, 变成求以 i 为结束点, 只进行 1 次交易获得的最大利润, 那么:
43+
44+
f[i] = max(
45+
f[i-1],
46+
prices[i] - min(prices[j] { j in [0, i-1] })
47+
)
48+
49+
这仍然是一个 O(N) 的解法, 因为 min(prices[j] { j in [0, i-1] }) 不需要遍历, 可以在递推过程中直接维护好
50+
51+
现在再推广到进行 K 次交易的情况, 那我们要求以 i 为结束点, 进行 k 次交易获得的最大利润, 这时有了变化, 我们可以在 j 之前再进行 K - 1 次交易:
52+
53+
f[k, i] = max(
54+
f[k, i-1],
55+
prices[i] + max(f[k-1, j] - prices[j]) { j in [0, i-1] } )
56+
)
57+
58+
显然, f[0, i] = 0, f[k, 0] = 0
59+
60+
这个算法可以形象地描述一下, 在 k = 1 时, 我们每次要找的就是 i 之前的最低谷点作为这次交易的开始点 j, 而当 k > 1 时,
61+
我们 i 之前就有可能已经进行过交易了, 这时我们在找开始点 j 时, 就要同时考虑 "直到 j 为止, k-1 次交易的最大收益" - "j 本身的值". 以此来找到一个最佳点 j
62+
63+
在实现时, 假如用 Bottom-Up 递推, 那么只需要维护一个 vec[i], 因为每轮递推时只会考虑上一轮的数据, 我们可以复用这个 O(N) 的额外存储空间
64+
65+
最后, 这题会给 k 非常大的 corner case, 实际上 k 大于 prices.len() / 2 后面就没有意义了, 可以 shortcut 掉(== 允许无穷次交易的场景), 下面写的比较糙,
66+
直接限制了一下循环次数, 实际跑的时候运行时间会长一点
67+
*/
68+
impl Solution {
69+
pub fn max_profit(k: i32, prices: Vec<i32>) -> i32 {
70+
if prices.is_empty() { return 0 }
71+
let max_trans = k as usize;
72+
let mut cache = vec![0; prices.len()];
73+
for _ in 0..usize::min(max_trans, prices.len() / 2 + 1) {
74+
// best_by_in 维护了考虑前 N 次交易的最佳的买入点, 即 max(f[k-1, j] - prices[j]) { j in [0, i-1] }
75+
let mut best_buy_in = cache[0] - prices[0];
76+
for i in 1..prices.len() {
77+
// 复用 vec 前暂存一下前一次的计算结果
78+
let temp = cache[i];
79+
cache[i] = i32::max(cache[i-1], best_buy_in + prices[i]);
80+
// 更新 best_buy_in
81+
best_buy_in = i32::max(best_buy_in, temp - prices[i]);
82+
}
83+
}
84+
return *cache.last().unwrap()
85+
}
86+
}
87+
88+
// submission codes end
89+
90+
#[cfg(test)]
91+
mod tests {
92+
use super::*;
93+
94+
#[test]
95+
fn test_188() {
96+
assert_eq!(Solution::max_profit(2, vec![3,2,6,5,0,3]), 7);
97+
}
98+
}

0 commit comments

Comments
 (0)