Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
188 changes: 188 additions & 0 deletions 122. Best Time To Sell and Buy Stock.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
### Step1

- 頭の中でグラフを思い浮かべた。今より前の時点のやつを売買することができる(ズル)ので、今の値と比較してどうかで決めれる
- is_havingのようなフラグ変数は管理しづらくなるだろうか。1個くらいならOK?

```python
python
class Solution:
def maxProfit(self, prices: List[int]) -> int:
profit = 0
is_having = False
prev_price = None
for price in prices:
if prev_price is None:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if prev_price is None:を使うのはループの1回目だけだと思いますので、prev_priceをprice[0]で初期化するのもアリかと思いました。prices空の場合の制御は必要になりますがループ内のコードは短くなると思います。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ループの方をprice[1]からスタートする感じでしょうか。
確かにその方がシンプルですね

pass
elif is_having and prev_price > price:
profit += prev_price
is_having = False
elif not is_having and prev_price < price:
profit -= prev_price
is_having = True
prev_price = price
if is_having:
profit += prev_price
is_having = False
return profit
```

- テストケースを考える(実は今まであんまりやってなかった😅)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

同じくです。自分も気をつけようと思います。

- []
- [5]
- [-4] (株価がマイナスはなさそう)
- [0]
- [4, 5]
- [5, 4]
- [3, 3]
- [4, 5, 4]
- [5, 4, 5]
- [4, 5, 5]
- [4, 4, 5]
- [5, 4, 4]
- [5, 5, 4]
- [0, 0, 0, 0, 0, 0, 0, 0,,,,]
- [2, 2, 2, 2,,,,,,,,]
- [4, 5, 4, 5, 4, 5, 4, 5,,,,,,,]
- [5, 4, 5, 4, 5, 4, 5, 4,,,,]
- [4, 5, 6, 5, 4, 5, 6, 5, 4,,,,,]
- [6, 5, 4, 5, 6, 5, 4, 5, 6,,,,,]
- [1, 2, 3, 4, 5, 6,,,,]
- [100, 99, 98, 97, ,,,,,,]
- くらいかな。うーん。
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

自分は以前、理事の川中さんが「テストケースはシステマティックに生成すると良い」とおっしゃっていたのを参考にテストケースを考えています

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ありがとうございます。
そうおっしゃってましたよね〜、あとは全ての行を通るケース、空とかですかねえ




https://discord.com/channels/1084280443945353267/1210494002277908491/1210793843260133477

https://github.com/fhiyo/leetcode/pull/39/files

[https://github.com/TORUS0818/leetcode/pull/40](https://github.com/TORUS0818/leetcode/pull/40#discussion_r1875358734)

- 関数を定める方法、DPでやる方法などがある
- 関数を定める方法で書いてみる
- find_next_peakとfind_next_bottomはよく似て関数だが、ここを無理やり共通化しようとするのは流石にややこしい?
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C++ には adjacent_find というのがありますね。
共通化しなくてもいいように思いますが。


```python
class Solution:
def find_next_peak(self, start_index: int, nums: List[int])-> Tuple[int, int]:
assert start_index < len(nums)
i = start_index
while i + 1 < len(nums) and nums[i] <= nums[i + 1]:
i += 1
return i, nums[i]

def find_next_bottom(self, start_index: int, nums: List[int])-> Tuple[int, int]:
assert start_index < len(nums)
i = start_index
while i + 1 < len(nums) and nums[i] >= nums[i + 1]:
i += 1
return i, nums[i]

def maxProfit(self, prices: List[int]) -> int:
assert(len(prices) > 0)
profit = 0
i = 0
bought_price = prices[0]
while i < len(prices) - 1:
peak_index, peak_price = self.find_next_peak(i, prices)
profit += peak_price - bought_price
bottom_index, bottom_price = self.find_next_bottom(peak_index, prices)
i = bottom_index
bought_price = bottom_price
return profit
```

-  https://github.com/hayashi-ay/leetcode/pull/56

- find_next_bottom関数の返り値、indexだけの方がシンプルで良いだろうか、でも値もあった方が便利そうでむずい
- https://github.com/Ryotaro25/leetcode_first60/pull/42
- price[i + 1] - prices[i] を変数に置いた方がいいのはなるほど
- 持ってる時、持ってない時で出す

```python

class Solution:
def maxProfit(self, prices: List[int]) -> int:
assert(len(prices) > 0)
profit_having_stock_prev = -prices[0]
profit_not_having_stock_prev = 0
for i in range(1, len(prices)):
profit_having_stock = max(profit_having_stock_prev, profit_not_having_stock_prev - prices[i])
profit_not_having_stock = max(profit_not_having_stock_prev, profit_having_stock_prev + prices[i])
profit_having_stock_prev = profit_having_stock
profit_not_having_stock_prev = profit_not_having_stock
return max(profit_having_stock_prev, profit_not_having_stock_prev)
```

```

- https://github.com/seal-azarashi/leetcode/pull/36
- 以下のコードでも通るっぽい。簡潔ではあるが、max(profit_not_having_stock, profit_having_stock + prices[i]) の時だけprofit_having_stockが実は同時点で、ただ前の行で更新されているならば-prices[i]と+prices[i]で打ち消しあって、前時点のprofit_not_having_stockに結果的になる、という推理が必要?

```python

class Solution:
def maxProfit(self, prices: List[int]) -> int:
assert(len(prices) > 0)
profit_having_stock= -prices[0]
profit_not_having_stock = 0
for i in range(1, len(prices)):
profit_having_stock = max(profit_having_stock, profit_not_having_stock - prices[i])
profit_not_having_stock = max(profit_not_having_stock, profit_having_stock + prices[i])
return max(profit_having_stock, profit_not_having_stock)
```

- https://github.com/goto-untrapped/Arai60/pull/59
- peakとbottomを交互にループするのは、最初にbottomをやった方が綺麗そう?気づかなかった。

```python
python

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

消し忘れでしょうか。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

そうですね、ありがとうございます(よくあるのでlinterとか導入したほうがいい気もしてきました)

class Solution:
def find_next_peak(self, start_index: int, nums: List[int])-> Tuple[int, int]:
assert start_index < len(nums)
i = start_index
while i + 1 < len(nums) and nums[i] <= nums[i + 1]:
i += 1
return i, nums[i]

def find_next_bottom(self, start_index: int, nums: List[int])-> Tuple[int, int]:
assert start_index < len(nums)
i = start_index
while i + 1 < len(nums) and nums[i] >= nums[i + 1]:
i += 1
return i, nums[i]

def maxProfit(self, prices: List[int]) -> int:
assert(len(prices) > 0)
profit = 0
i = 0
while i + 1 < len(prices):
bottom_i, bottom_price = self.find_next_bottom(i, prices)
peak_i, peak_price = self.find_next_peak(bottom_i, prices)
profit += peak_price - bottom_price
i = peak_i
return profit
```

- https://github.com/sakupan102/arai60-practice/pull/39

https://github.com/shining-ai/leetcode/pull/38

https://github.com/hroc135/leetcode/pull/36/files  

- profit_having_stockとprofit_not_having_stockを同時に更新するというてもあるか。ただ1行が長くなって見にくくなりそう。

## Step3

これが自然かな

```python
class Solution:
def maxProfit(self, prices: List[int]) -> int:
profit = 0
for i in range(len(prices) - 1):
diff = prices[i + 1] - prices[i]
if diff > 0:
profit += diff
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ifを消して
profit += max(0, diff) でもよさそうです。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

確かに。ありがとうございます。
簡潔な一方、やや読み手に推理が必要な気もしますが、これぐらいなら許容範囲内ですかね

return profit
```