-
Notifications
You must be signed in to change notification settings - Fork 0
213 house robber ii medium #6
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,38 @@ | ||
| # 問題へのリンク | ||
| [House Robber II - LeetCode](https://leetcode.com/problems/house-robber-ii/description/) | ||
|
|
||
| # 言語 | ||
| Python | ||
|
|
||
| # 問題の概要 | ||
| この問題は、円形の家のリストが与えられたときに、隣接する家を盗むことができない条件の下で、最大の金額を盗む方法を見つけることです。 | ||
| 家が一列に並んでいる場合の最大金額を求める問題[House Robber - LeetCode](https://leetcode.com/problems/house-robber/description/)と似ていますが、円形の配置により、最初と最後の家は隣接しています。つまり、最初の家と最後の家の両方を盗むことはできないという制約が追加されています。 | ||
|
|
||
|
|
||
|
|
||
| # 自分の解法 | ||
| 以下の二つの場合に分けて考えます。円形の制約から、求める答えは以下の二つのケースのうちいずれかになります。 | ||
| 1. 最初の家を盗まない場合(最後の家は盗んでもよい) | ||
| 2. 最後の家を盗まない場合(最初の家は盗んでもよい) | ||
|
|
||
| そして、それぞれのケースに対して、通常の「House Robber」の問題を動的計画法で解くことで、最大金額を求めます。動的計画法の際には、二つの変数の値だけを逐次更新していくことで、空間計算量を`O(1)`に抑えます。 | ||
|
|
||
|
|
||
| - 時間計算量:`O(len(nums))` | ||
| - 空間計算量:`O(1)` | ||
|
|
||
| ## step2 | ||
| - `rob_houses_linear`をprivateな関数`_rob_houses_linear`に変更しました | ||
| - 変数の更新の際に使う変数`tmp`を`prev_tmp`に変更しました | ||
| - `tmp`という変数が一般に良くないというのはわかりつつも、このような一時的に値を保持して数行以内で使われるような変数ではむしろわかりやすいのでは無いかと思っています。 | ||
|
|
||
|
|
||
|
|
||
| ## step3 | ||
| - dpの漸化式をコメントに書きました | ||
| - `dp[i] = max(dp[i-1], dp[i-2] + nums[i])` | ||
| - `dp[i]`は`i`番目の家まで考えたときの最大金額を表します。 | ||
|
|
||
| # 次に解く問題の予告 | ||
| - [ ] [Evaluate Division - LeetCode](https://leetcode.com/problems/evaluate-division/description/) | ||
| - [ ] [Subsets - LeetCode](https://leetcode.com/problems/subsets/) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| # | ||
| # @lc app=leetcode id=213 lang=python3 | ||
| # | ||
| # [213] House Robber II | ||
| # | ||
|
|
||
|
|
||
| # @lc code=start | ||
| class Solution: | ||
| def rob(self, nums: list[int]) -> int: | ||
| if not nums: | ||
| return 0 | ||
| elif len(nums) == 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. 直前に return 0 があるため、 if 文にしてしまってもよいと思います。
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. レビューありがとうございます。 |
||
| return nums[0] | ||
|
|
||
| max_rob_amount_excluding_first = self.rob_houses_linear(nums[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_rob_amount_excluding_last = self.rob_houses_linear(nums[: len(nums) - 1]) | ||
| return max(max_rob_amount_excluding_first, max_rob_amount_excluding_last) | ||
|
|
||
| def rob_houses_linear(self, nums: list[int]) -> int: | ||
| """ | ||
| solve house robber problem when houses are arranged linear (not in a circle) | ||
| """ | ||
| if not nums: | ||
| return 0 | ||
| elif len(nums) == 1: | ||
| return nums[0] | ||
|
|
||
| previous_rob_amount = nums[0] | ||
| current_rob_amount = max(previous_rob_amount, nums[1]) | ||
| for i in range(2, len(nums)): | ||
| tmp = current_rob_amount | ||
| current_rob_amount = max(current_rob_amount, previous_rob_amount + nums[i]) | ||
| previous_rob_amount = tmp | ||
| return max(previous_rob_amount, current_rob_amount) | ||
|
|
||
|
|
||
| # @lc code=end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| # | ||
| # @lc app=leetcode id=213 lang=python3 | ||
| # | ||
| # [213] House Robber II | ||
| # | ||
|
|
||
|
|
||
| # @lc code=start | ||
| class Solution: | ||
| def rob(self, nums: list[int]) -> int: | ||
| """ | ||
| solve house robber problem when houses are arranged in a circle | ||
| """ | ||
| if not nums: | ||
| return 0 | ||
| elif len(nums) == 1: | ||
| return nums[0] | ||
|
|
||
| max_rob_amount_excluding_first = self._rob_houses_linear(nums[1:]) | ||
| max_rob_amount_excluding_last = self._rob_houses_linear(nums[: len(nums) - 1]) | ||
| return max(max_rob_amount_excluding_first, max_rob_amount_excluding_last) | ||
|
|
||
| def _rob_houses_linear(self, nums: list[int]) -> int: | ||
| """ | ||
| solve house robber problem when houses are arranged linear (not in a circle) | ||
| """ | ||
| if not nums: | ||
| return 0 | ||
| elif len(nums) == 1: | ||
| return nums[0] | ||
|
|
||
| previous_rob_amount = nums[0] | ||
| current_rob_amount = max(previous_rob_amount, nums[1]) | ||
| for i in range(2, len(nums)): | ||
| prev_tmp = current_rob_amount | ||
| current_rob_amount = max(current_rob_amount, previous_rob_amount + nums[i]) | ||
| previous_rob_amount = prev_tmp | ||
|
|
||
| return current_rob_amount | ||
|
|
||
|
|
||
| # @lc code=end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| # | ||
| # @lc app=leetcode id=213 lang=python3 | ||
| # | ||
| # [213] House Robber II | ||
| # | ||
|
|
||
|
|
||
| # @lc code=start | ||
| class Solution: | ||
| def rob(self, nums: list[int]) -> int: | ||
| if not nums: | ||
| return 0 | ||
| elif len(nums) <= 2: | ||
| return max(nums) | ||
|
|
||
| rob_amount_excluing_first = self._rob_houses_linear(nums[1:]) | ||
| rob_amount_excluing_last = self._rob_houses_linear(nums[: len(nums) - 1]) | ||
|
|
||
| max_rob_amount = max(rob_amount_excluing_first, rob_amount_excluing_last) | ||
| return max_rob_amount | ||
|
|
||
| def _rob_houses_linear(self, nums: list[int]) -> int: | ||
| if not nums: | ||
| return 0 | ||
|
|
||
| # dp[i]: the maximum robbed amount from first i houses; 0, ..., i-1 | ||
| # dp[i] = max(dp[i - 1], dp[i - 2] + nums[i]) | ||
| # use 1-dimensional dynamic programming for efficient memory, | ||
| previous_max_rob_amount = nums[0] | ||
| max_rob_amount = max(previous_max_rob_amount, nums[1]) | ||
| for num in nums[2:]: | ||
| tmp = max_rob_amount | ||
| max_rob_amount = max(max_rob_amount, previous_max_rob_amount + num) | ||
| previous_max_rob_amount = tmp | ||
|
|
||
| return max_rob_amount | ||
|
|
||
|
|
||
| # @lc code=end |
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.
単に dp で、dynamic programming だとはあまり通じない気がします。
たとえば、Chrome のソースコードで dp で調べた結果です。
https://source.chromium.org/search?q=%5Cbdp%5Cb&sq=
あと、最近の LLM、プロダクションのコードにしてくれと頼むときれいにしてくれます。
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.
レビューありがとうございます。
dpは余り浸透していない、いわば競プロの方言なのですね、気をつけます。いつもLLMにコードレビューは頼んでいたのですが、その指示の出し方はしたこと無かったので参考になります。ありがとうございます。