-
Notifications
You must be signed in to change notification settings - Fork 0
Solved Arai60/1011. Capacity To Ship Packages Within D Days #44
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,131 @@ | ||
## Step 1. Initial Solution | ||
|
||
- シンプルに可能性のある値からスタートして実験して成功しなければ+1してみる | ||
- 可能性のある値としては、最大値と合計/日数の大きい方 | ||
- 本当は+1していくよりも効率的なやり方があるような気もするが一旦これで実装 | ||
|
||
```python | ||
class Solution: | ||
def shipWithinDays(self, weights: List[int], days: int) -> int: | ||
min_capacity = max(max(weights), sum(weights) // days) | ||
daily_capacity = min_capacity | ||
days_left = days | ||
while True: | ||
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. min_capacityはループの中で必ず1ずつ増えていくので自分ならforループにするかなと思いました |
||
for weight in weights: | ||
if daily_capacity < weight: | ||
days_left -= 1 | ||
daily_capacity = min_capacity | ||
if days_left == 0: | ||
break | ||
daily_capacity -= weight | ||
if days_left > 0: | ||
return min_capacity | ||
days_left = days | ||
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. 自分はこれはforループの前の初期化のイメージなので |
||
min_capacity += 1 | ||
daily_capacity = min_capacity | ||
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. 23行目と同様に |
||
``` | ||
|
||
### Complexity Analysis | ||
|
||
- 時間計算量:O(xn) | ||
- 答えを見つけるまでにwhile文を回す回数x | ||
- 最大いくつまで行けるかはよく分かっていなかったが重量の合計まで上げれば確実にOK | ||
- 重量のリスト長n | ||
- 空間計算量:O(1) | ||
- 追加で必要な変数は3個分 | ||
|
||
## Step 2. Alternatives | ||
|
||
- max(weights)とsum(weights)の間に答えがあると考えられる(!)のでこれを用いて二分探索する方法がある | ||
- これがどれだけ速いのかをよく理解できていない | ||
- 元々期待値として線形に船の容量を上げるとどうなるのか? | ||
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. 探索の範囲を具体的に考えてみるとどうでしょうか。例えば長さ10000の範囲だとして、線形探索では最悪何ステップかかって二分探索だと最悪何ステップになるか、という具体例を考えてみるとどれだけ速くなるかが定量的につかめるかなと思います。 |
||
- bisect_leftの引数に自分で定義した関数を渡す方法 | ||
- https://github.com/tokuhirat/LeetCode/pull/44/files#diff-06f87195f0dc2635b3c326a3257c212b916a27bd5f47afbf0368d37831b513a3R29 | ||
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. このような方法があるのですね、これは勉強になりました! |
||
- 個人的にはまだ慣れていないがシンプルに書けていると感じる | ||
- rangeをbisect_leftの探索対象のリストに入れている | ||
- 省メモリ | ||
- https://github.com/Fuminiton/LeetCode/pull/44/files#r2129170877 | ||
- 他にも同じような発想が多い | ||
- https://github.com/Fuminiton/LeetCode/pull/44/files | ||
- https://github.com/nittoco/leetcode/pull/47/files | ||
- いつも思うがなぜ皆同じような解法になっているんだろうか?自分だけ何かを理解できていないような気分 | ||
- 取りあえず今回のような範囲を思いつきにくい場合でも二分探索を軸に考えるのは一般的と理解しておく | ||
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. イメージとしてはどっかでTrue/Falseが入れ替わる境界があるときに二分探索が使えそうだな~という発想になるのかなと思っています。 今回のケースで言うと「days日間でcapacityの船で全ての積み荷を輸送できるか?」というブール値がcapacityを増やしていくと |
||
- days=0の時の考慮 | ||
- 無限ループになってしまう | ||
- 初めに弾いておくべき | ||
- https://github.com/nittoco/leetcode/pull/47/files#diff-4e146417f14c744a10f851601f26cd2cb17b420ff966720e568f6f5679aa475eR79 | ||
- こういう秒数の見積もりを自然にやりたい | ||
- https://github.com/Fuminiton/LeetCode/pull/44/files#diff-26f729e002c5c9244ef5172608184baffaf2040258f34729cff27b841079b595R18 | ||
- 二分探索での実装 | ||
|
||
```python | ||
class Solution: | ||
def shipWithinDays(self, weights: List[int], days: int) -> int: | ||
if not weights: | ||
return 0 | ||
if days <= 0: | ||
return -1 | ||
|
||
def canShipWithCapacity(capacity: int) -> bool: | ||
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. inner function の関数名は、 lower_snake で書くことが多いと思います。 LeetCode が指定している関数名は仕方ないと思います。 参考までにスタイルガイドへのリンクを貼ります。 https://peps.python.org/pep-0008/#function-and-variable-names
https://google.github.io/styleguide/pyguide.html#316-naming
上記のスタイルガイドは唯一絶対のルールではなく、複数あるスタイルガイドの一つに過ぎないということを念頭に置くことをお勧めします。また、所属するチームにより何が良いとされているかは変わります。自分の中で良い書き方の基準を持ちつつ、チームの平均的な書き方で書くことをお勧めいたします。 |
||
days_left = days | ||
daily_capacity_left = capacity | ||
for weight in weights: | ||
if daily_capacity_left < weight: | ||
days_left -= 1 | ||
daily_capacity_left = capacity | ||
if days_left == 0: | ||
return False | ||
daily_capacity_left -= weight | ||
return True | ||
|
||
left = max(weights) | ||
right = sum(weights) | ||
Comment on lines
+81
to
+82
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. left, rightがどのような不変条件を満たしているかを言語化してみて変数名をつけるとより分かりやすくなるのかなと思いました。 |
||
while left < right: | ||
middle = (left + right) // 2 | ||
if canShipWithCapacity(middle): | ||
right = middle | ||
else: | ||
left = middle + 1 | ||
return left | ||
``` | ||
|
||
|
||
## Step 3. Final Solution | ||
|
||
- 自作関数をbisect_leftに渡すのを練習 | ||
- loを使わない方法としては以下がある | ||
|
||
```python | ||
return bisect_left( | ||
range(max(weights), sum(weights) + 1), | ||
True, | ||
key=canShipWithCapacity | ||
) + max(weights) | ||
``` | ||
|
||
|
||
```python | ||
class Solution: | ||
def shipWithinDays(self, weights: List[int], days: int) -> int: | ||
if days <= 0: | ||
return -1 | ||
if not weights: | ||
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. おそらくL128のmaxでエラーを出さないために入れていると思うのですが、maxにdefault引数を指定してこれは消してしまっても良いと思います。 |
||
return 0 | ||
|
||
def canShipWithCapacity(capacity: int) -> bool: | ||
daily_load = 0 | ||
required_days = 1 | ||
for weight in weights: | ||
daily_load += weight | ||
if daily_load > capacity: | ||
required_days += 1 | ||
daily_load = weight | ||
return required_days <= days | ||
|
||
return bisect_left( | ||
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. 面白い書き方だと思いました。 sequence = range(max(weights), sum(weights) + 1)
index = bisect_left(sequence, True, key=canShipWithCapacity)
return sequence[index] このようにしてしまう選択肢もあると思いました。 |
||
range(sum(weights) + 1), | ||
True, | ||
lo=max(weights), | ||
key=canShipWithCapacity | ||
) | ||
``` |
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.
私はぱっと見だと分からず少し思考が必要だったので、単にmax(weights)という明らかに最小だと分かるものにしておくのもよいかもしれません。
ただ、可能な限り大きい値にしておけばループの回数も減るのでその辺はトレードオフになりそうです。
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.
常に
max(weights)
>=sum(weights) // days
が成立するので、いずれにせよ冗長だと思います。