LC309
背景
私はこのテーマに長い間悩んできました。解決策のアイデアをたくさん読んだ後でも、まだ明確に説明できていないことがあります。
ようやく、この考えを明確に説明できるものを見つけました(抜け穴はないようです):
他の質問の答えは正しいかもしれませんが、必要な説明がいくつか省略されており、理解にギャップが生じています
簡単ではない
おそらくトピック自体は特に明確ではありません。ここに追加します:
对中间的某一天而言,是否存在除了:买入 / 卖出 / 冷冻期 之外的第四种情况——无操作?
答案是:可以存在。
一連の考え
次に、この問題を解決するためのアイデアを話します。
[i 日目に株式を保有するかどうか] という状況に応じて大まかに議論し、動的計画法を使用します。重要なのは、状況について話し合い、十分に明確にすることです。!
2 つの制約:
- 購入後、購入を続ける前に売却する必要があります
- 販売後すぐに購入することはできず、少なくとも 1 日は冷凍する必要があります (1 日冷凍して買わないことを続けることもできます。これは、複数日冷凍することに相当します!!!)。
状況に応じて相談する
# 步骤1: 考虑第 i 天,总共存在两种状态:持有股票 / 不持有 股票,分别记做 dp[i][0],dp[i][1]。i=0时,对应的利润分别为:-prices[0]、0
dp[i]表示第i天的最大利润,dp[i] = max(dp[i][0],dp[i][1])
# 步骤2: 根据第i天的状态,考虑第 i-1天从什么状态 转换得到 第 i天的状态
对于 dp[i][0] 再分两种情况:
1)第i-1天不持有(冷冻期/无操作),注意!!此时第i-2天肯定是不持有,否则要么变成2)的情况重复考虑,要么在i-1天卖出,导致第i天处于冷冻期而无法持有(不能得到dp[i][0]的状态)
2)第i-1天持有 【第i-2天可以不持有,也可以不持有】
对于 dp[i][1] 也分两种情况:
3)第i-1天持有,第i天卖出【第i-2天可以持有,也可以不持有】
4)第i-1天也不持有,第i天无操作【第i-2天可以持有,也可以不持有】
为什么其他3种情况:2)、3)、4)不考虑第i-2天的情况??
因为其他三种情况对第i-2天没有要求!
合法的状态流转顺序包括:
2)持有/不持有->持有->持有;
3)持有/不持有->持有->不持有;
4)持有/不持有->不持有->不持有;
唯一不合法的是:持有 -> 不持有 -> 持有 ,对应情况1)!!!
所以情况1肯定是:不持有->不持有->持有
上記 4 つの状況に対応する状態遷移方法と、対応する i 日目の利益はそれぞれ次のとおりです。
1)dp[i-1][1] -> dp[i][0],dp[i-2][1]-prices[i] # 注意这里需要考虑第i-2天的约束!!而不是 dp[i-1][1]-prices[i]
2)dp[i-1][0] -> dp[i][0],dp[i-1][0]
3)dp[i-1][0] -> dp[i][1],dp[i-1][0]+ prices[i]
4)dp[i-1][1] -> dp[i][1],dp[i-1][1]
コード
class Solution:
def maxProfit(self, prices: List[int]) -> int:
n = len(prices)
if n < 2: return 0
dp = [[0,0] for _ in range(n)]
# dp[0][0]表示持有,dp[0][1]表示不持有
dp[0][0] = -prices[0]
dp[0][1] = 0
# 因为下标从2开始,所以需要计算下标为0和1处的初始值,for循环从2到n-1
# 另一种写法是:数组长度设为n+1,只计算下标为1的初始值,for循环从2到n。减少了初始值计算但是可读性没有前一种强
dp[1][0] = max(dp[0][1]-prices[1], dp[0][0])
dp[1][1] = max(dp[0][0]+prices[1], dp[0][1])
for i in range(2,n): # 注意i-2,需要从2开始循环
dp[i][0] = max(dp[i-2][1]-prices[i],dp[i-1][0])
dp[i][1] = max(dp[i-1][0]+prices[i],dp[i-1][1])
# print(dp)
return dp[n-1][1]
要約する
問題を解決するための重要なポイント:
- 中日には、3 つの状態ではなく、4 つの状態 (買い、売り、凍結期間、操作なし) を含めることができます。
- 毎日のステータスは、開催するか開催しないかの 2 つの状況によってのみ決定され、それ以外の状況は存在しません。このうち、本来保有には買い、保有継続(無運用)、非保有には売り、保有継続(無運用)、凍結期間が含まれます。前述の 2 つの状態のみに従って分割すると、問題が単純化され、すべての状況を同時に考慮することによって引き起こされる思考の混乱が回避されます。
- 状態移行の場合:i-1日目からi日目への状態移行を考える場合、特別な考慮が必要:i-1日目の保有なしからi日目の保有へ移行する場合この状況では、i-2 日も必要です。他の 3 つの州の移行については、i-2 日の州の要件はありません。
- forループで状態を再帰する場合、境界処理(2日目から)と初期値(0日目と1日目)の計算に注意が必要です。
- 最終日に開催しないと最大の利益が得られません