-
Notifications
You must be signed in to change notification settings - Fork 0
122. Best Time to Buy and Sell Stock II #40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,176 @@ | ||
| # Step1 | ||
|
|
||
| かかった時間:5min | ||
|
|
||
| 計算量:prices.length=Nとして | ||
|
|
||
| 時間計算量:O(N) | ||
|
|
||
| 空間計算量:O(1) | ||
|
|
||
| ```python | ||
| class Solution: | ||
| def maxProfit(self, prices: List[int]) -> int: | ||
| prev_price = prices[0] | ||
| max_profit = 0 | ||
| for price in prices: | ||
| if prev_price < price: | ||
| max_profit += (price - prev_price) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 右辺の () は外してよいと思います。
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 無意識で付けてしまいました。。 |
||
| prev_price = price | ||
|
|
||
| return max_profit | ||
| ``` | ||
| 思考ログ: | ||
| - pricesのi番目の要素とi+1番目の要素の関係に注目する | ||
| - prices[i] < prices[i + 1]: prices[i]で買ってprices[i+1]で売る | ||
| - prices[i] >= prices[i + 1 ]: 何もしない | ||
| - i番目で買ってi+k番目で売るのは、buy(i) & sell(i + 1) & buy(i + 1) & sell(i + 2) & buy(i + 2)... & buy(i + k - 1) & sell(i + k)と同じなので分解して一つずつ考える | ||
|
|
||
| ```python | ||
| class Solution: | ||
| def maxProfit(self, prices: List[int]) -> int: | ||
| return sum([ | ||
| prices[i] - prices[i - 1] for i in range(1, len(prices)) | ||
| if prices[i] - prices[i - 1] > 0 | ||
| ]) | ||
| ``` | ||
| 思考ログ: | ||
| - やっていることは同じ、差分をとってプラスのものだけ足して返す | ||
| - 簡潔に書けるが、一度```price_diff```のような変数を介してあげた方が意図は取りやすいと思う(あと同じ計算している) | ||
|
|
||
| # Step2 | ||
|
|
||
| 講師役目線でのセルフツッコミポイント: | ||
|
|
||
| 参考にした過去ログなど: | ||
| - https://github.com/Ryotaro25/leetcode_first60/pull/42 | ||
| - https://github.com/seal-azarashi/leetcode/pull/36 | ||
| - https://github.com/goto-untrapped/Arai60/pull/59 | ||
| - top-down DP | ||
| - これが一番理解に時間がかかった | ||
| - https://github.com/Yoshiki-Iwasa/Arai60/pull/53 | ||
| - 追加課題:最大利益を実現できる最小の売買回数は? | ||
| - https://github.com/fhiyo/leetcode/pull/39 | ||
| - https://github.com/sakupan102/arai60-practice/pull/39 | ||
| - https://github.com/shining-ai/leetcode/pull/38 | ||
| - https://github.com/hayashi-ay/leetcode/pull/56 | ||
| - 2ndの解法の関数の切り分けが勉強になった(同様のロジックを書く際に自分でもこのように分けて整理できるか) | ||
| - https://discord.com/channels/1084280443945353267/1210494002277908491/1210521728699076628 | ||
|
|
||
| DP | ||
| ```python | ||
| class Solution: | ||
| def maxProfit(self, prices: List[int]) -> int: | ||
| # pl: profit & lossの略語 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. profitだけで良いと思います
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. profitという変数でマイナスの値を取ったら、損失だなーというのは分かると思いますが、そういう話ではないですか?自分はprofit_lossという変数がたとえば5だとしてこれがprofitなのかlossなのか分からなくて混乱してしまいます
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 収益計算の結果にprofit and loss(pl)という表現をよく使うので私にとってはこちらの方が馴染みはあります。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 林さんの意見に+1。利益がマイナスだと、赤字の意味だと思います。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 例えば、
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. コメント有難うございます。 |
||
| max_pl_hold_last = -prices[0] | ||
| max_pl_no_position_last = 0 | ||
| for i in range(1, len(prices)): | ||
| max_pl_hold = max( | ||
| max_pl_hold_last, | ||
| max_pl_no_position_last - prices[i] # 購入 | ||
| ) | ||
| max_pl_no_position = max( | ||
| max_pl_hold_last + prices[i], # 売却 | ||
| max_pl_no_position_last | ||
| ) | ||
| max_pl_hold_last = max_pl_hold | ||
| max_pl_no_position_last = max_pl_no_position | ||
|
|
||
| return max_pl_no_position_last | ||
| ``` | ||
| 思考ログ: | ||
| - 他の人の解答を漁っていて見つけた、株を持っている状態かどうかで場合わけ | ||
| - 最初よく分からなかったので自分なりに書いてみた | ||
| - 株を持っている場合は以下の大きい方を採用 | ||
| - 前回株を持っている状態での最大利得をそのまま引き継ぐ(今回は何もしない) | ||
| - 前回株を持っていない状態での最大利得に今回株を新規購入した分のお金を引いたもの | ||
| - 株を持っていない場合は以下の大きい方を採用 | ||
| - 前回株を持っている状態での最大利得に今回株を決済した分のお金を追加したもの | ||
| - 前回株を持っていない状態での最大利得をそのまま引き継ぐ(今回は何もしない) | ||
| - あんまり専門用語を使うのは憚られるが、そういう現場にいるという体で```no_position```は```square```とかでも良かったかも | ||
|
|
||
| 素直にvalley-to-peakをとっていく方法 | ||
| ```python | ||
| class Solution: | ||
| def maxProfit(self, prices: List[int]) -> int: | ||
| def find_next_valley(start: int) -> int: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 自分ならwhileの中のifもwhileに含めるかなと思います。 def find_next_valley(start: int) -> int:
i = start
while i < len(prices) - 1 and prices[i] >= prices[i + 1]:
i += 1
return i
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 有難うございます。 |
||
| i = start | ||
| while i < len(prices) - 1: | ||
| if prices[i] >= prices[i + 1]: | ||
| i += 1 | ||
| else: | ||
| return i | ||
| return i | ||
|
|
||
| def find_next_peak(start: int) -> int: | ||
| i = start | ||
| while i < len(prices) - 1: | ||
| if prices[i] <= prices[i + 1]: | ||
| i += 1 | ||
| else: | ||
| return i | ||
| return i | ||
|
|
||
| i = 0 | ||
| max_profit = 0 | ||
| while i < len(prices): | ||
| valley_i = find_next_valley(i) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i は略さず index としたほうが、読むにあたり認知負荷が低くなると思います。 |
||
| buy_price = prices[valley_i] | ||
| peak_i = find_next_peak(valley_i) | ||
| sell_price = prices[peak_i] | ||
| max_profit += sell_price - buy_price | ||
| i = peak_i + 1 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. peak_iの探索はvalley_iのちょうどのindexから始まってるので(L119)、自分ならこっちもi = peak_iにして統一感を出し、while i < len(prices) - 1とします。 |
||
|
|
||
| return max_profit | ||
| ``` | ||
| 思考ログ: | ||
| - 以前はこういう処理を関数に分けずに書き連ねてバグらせる、というのがよくあった | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 複雑さへの対処ができていないイメージですね。
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 言語化有難うございます。 まだ完全ではないですが、上記のようなことを意識してから明らかに解ける問題が増えました。 |
||
| - ```valley_i```とか```peak_i```のように名前を変えずに```i```を更新していく感じでもいいのだろうか | ||
| - ```find_next_valley/peak```は、```find_next_valley/peak_index```のように目的語を入れた方がいい?自明か? | ||
| - ```find_next_valley/peak```は、else以下をbreakしてreturnをまとめるでも良かったか | ||
| - 同値や端っこの処理はちゃんと考えないと(選択しないと)いけない | ||
| - ```if prices[i] <= prices[i + 1]:```とか```=```を入れるかどうか、とか | ||
| - [追加課題](https://github.com/Yoshiki-Iwasa/Arai60/pull/53/files#r1730194725)は、この実装で自然に答えられる | ||
|
|
||
| 再帰(top-down) | ||
| ```python | ||
| class Solution: | ||
| def maxProfit(self, prices: List[int]) -> int: | ||
| def max_profit_helper(day: int, price_tomorrow: int, max_profit: int) -> int: | ||
| if day < 0: | ||
| return max_profit | ||
|
|
||
| price_today = prices[day] | ||
| max_profit += max(0, price_tomorrow - price_today) | ||
|
|
||
| return max_profit_helper(day - 1, price_today, max_profit) | ||
|
|
||
| return max_profit_helper(len(prices) - 2, prices[len(prices) - 1], 0) | ||
| ``` | ||
| 思考ログ: | ||
| - やっていることが分かりやすくなるよう、少し持ち物を冗長にした(本当はdayとmax_profitだけでいい) | ||
| - 明日から来た自分から今日の日付と明日の株価と明日以降達成できる最大収益を教えてもらう | ||
|
|
||
| # Step3 | ||
|
|
||
| かかった時間:2min | ||
|
|
||
| ```python | ||
| class Solution: | ||
| def maxProfit(self, prices: List[int]) -> int: | ||
| prev_price = prices[0] | ||
| max_profit = 0 | ||
| for price in prices: | ||
| max_profit += max(0, price - prev_price) | ||
| prev_price = price | ||
|
|
||
| return max_profit | ||
| ``` | ||
| 思考ログ: | ||
| - DPも面白いけど、こちらの方が自然な感じはする | ||
|
|
||
| # Step4 | ||
|
|
||
| ```python | ||
| ``` | ||
| 思考ログ: | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
prev_price を最初の price で初期化している点に違和感を感じました。
prev_price < priceで処理がスキップされるところまで理解できれば、正しく動くことが分かるのですが、ややパズルに感じました。やや仰々しいですが、最初の日かどうかを表すフラグを立てるのはいかがでしょうか?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
有難うございます。
完全にパズルですね。。
最初はprices[1]から処理するようなコードを書いていたのですが、まとめてできないか検討したところ、prev_price = prices[0]とおけば問題なく動くことが分かり、記述もシンプルになることからこのように変更しました。
が、その結果、自分が解いたパズルをコードに埋め込むことになってしましました。。