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
95 changes: 95 additions & 0 deletions 213. House Robber II/213. House Robber II.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# 213. House Robber II
## STEP1
- 何も見ずに解いてみる
- 0番目と n-1 番目を考えるとややこしいので、場合分けして直線のケースで解けばよさそう。
```python
class Solution:
def rob(self, nums: List[int]) -> int:
if len(nums) <= 1:
return nums[0]

def rob_linear(nums: List[int]) -> int:
max_gain_robbed = nums[0]
max_gain_skipped = 0
for i in range(1, len(nums)):
prev_max_gain_robbed = max_gain_robbed
max_gain_robbed = max_gain_skipped + nums[i]
max_gain_skipped = max(max_gain_skipped, prev_max_gain_robbed)
return max(max_gain_skipped, max_gain_robbed)

return max(rob_linear(nums[:-1]), rob_linear(nums[1:]))
```
#### memo
時間計算量: $O(n)$
空間計算量: $O(1)$

## STEP2
### プルリクやドキュメントを参照
- https://github.com/olsen-blue/Arai60/pull/36/files
- 関数名の inline は関数のインライン化が想起されそう。in_line であればOKか。上のSTEP1で書いた rob_linear は微妙で rob_linearly の方がよさそう。
- https://github.com/olsen-blue/Arai60/pull/36/files#r1974943323
> begin <= x < end の区間とする
- なるほど。シンプルになってすごい。

再帰バージョンを書いてみる。区間の端のうち含まれる start を動かした方がわかりやすいと思いました。
```python
import functools


class Solution:
def rob(self, nums: List[int]) -> int:
if len(nums) == 1:
return nums[0]

@functools.cache
def rob_from_index(start: int, stop: int) -> int:
length = stop - start
if length == 1:
return nums[start]
if length == 2:
return max(nums[start], nums[start + 1])
return max(
rob_from_index(start + 2, stop) + nums[start],
rob_from_index(start + 1, stop),
)

return max(rob_from_index(0, len(nums) - 1), rob_from_index(1, len(nums)))
```

- https://github.com/hayashi-ay/leetcode/pull/50/files
- 関数内関数を書かないと微妙に異なる処理を読み解かないといけないので読みにくさを感じた。
- 198\. House Robber の内容ですが、nums[0] に関する処理もループに入れられる。
```python
def rob_linear(nums: List[int]) -> int:
max_gain_robbed = 0
max_gain_skipped = 0
for i in range(len(nums)):
prev_max_gain_robbed = max_gain_robbed
max_gain_robbed = max_gain_skipped + nums[i]
max_gain_skipped = max(max_gain_skipped, prev_max_gain_robbed)
return max(max_gain_skipped, max_gain_robbed)
```
- 関数内関数の引数は nums で良いか微妙。nums 自体を渡しているわけではないため。money_list のようにした方が良いかもしれない。
- len(nums) == 0 の場合は IndexError でよいと思っています。奪う家がない状況で 0円得られますというのは違いそう。ValueError にした方が呼び出し元にとってはわかりやすいかもしれない。結局はこの関数の使用想定によって異なるがあまりイメージできなかった。

## STEP3
### 3回ミスなく書く
```python
class Solution:
def rob(self, nums: List[int]) -> int:
if len(nums) <= 1:
return nums[0]

def rob_linearly(nums: List[int]) -> int:
max_gain_robbed = 0
max_gain_skipped = 0
for i in range(len(nums)):
prev_max_gain_robbed = max_gain_robbed
max_gain_robbed = max_gain_skipped + nums[i]
max_gain_skipped = max(max_gain_skipped, prev_max_gain_robbed)
return max(max_gain_skipped, max_gain_robbed)

return max(rob_linearly(nums[:-1]), rob_linearly(nums[1:]))
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

自分もこの実装が、わかりやすくて良いかと思いました。
特に問題ないと思います。

```

3分,3分,2分で3回Accept