-
Notifications
You must be signed in to change notification settings - Fork 0
【Grind75Hard】7問目 1235. Maximum Profit in Job Scheduling #66
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,31 @@ | ||
| # startTimeでソートして、繋げられるものを順番に確認していく | ||
| # 終了時刻の早いものから見ていき、最大の利益になるものを選択していく | ||
| # (最大の利益にならないものは、それ以降でも最大にならないため捨てて良い) | ||
|
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. あるジョブを開始する際、その時刻以前に終了したジョブの中で、最も利益の総和が大きかったものを、次以降のジョブを開始する際の選択肢として残しておく、という点がポイントのように思いました。その部分についてコメントしてあるとよいと思いました。 |
||
| # 例:(start, end, profit) = ((1, 3 50), (2, 3, 10), (3, 5, 40), (4, 6, 70)) | ||
| # 3項目の選択肢は | ||
| # (1, 3, 50) -> (3, 5, 40)の順番で選択すると最大の利益になる | ||
| # (2, 3, 10) -> (3, 5, 40)は最大の利益にならない(4項目以降でも最大の利益にならないので捨てる) | ||
| # 4項目の選択肢は | ||
| # (1, 3, 50) -> (4, 6, 70)の順番で選択すると最大の利益になる | ||
| # (2, 3, 10)は3項目の検討時に捨てた | ||
| class Solution: | ||
| def jobScheduling( | ||
| self, startTime: List[int], endTime: List[int], profit: List[int] | ||
| ) -> int: | ||
| sorted_starts = sorted([(startTime[i], i) for i in range(len(startTime))]) | ||
| max_profit = 0 | ||
| end_profit_heap = [(0, 0)] | ||
| for start, i in sorted_starts: | ||
|
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. for文の中だけでも、nestが深くなる場合にはちゃんと名付けたほうが良さそうということですね。 |
||
| max_profit_use_i = 0 | ||
|
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 を使う最大の利益」となっており、変数の中に何が格納されているのか分かりにくく感じました。「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でも情報量変わらないかと思いましたが、ちゃんと単語で書かないと伝わらないですね。 |
||
| pre_info = () | ||
|
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. 確かに中身が分からず、何を表しているか読み取るのが大変ですね。 |
||
| while end_profit_heap and end_profit_heap[0][0] <= start: | ||
| pre_end, pre_profit = heapq.heappop(end_profit_heap) | ||
| if pre_profit + profit[i] <= max_profit_use_i: | ||
| continue | ||
| max_profit_use_i = pre_profit + profit[i] | ||
| pre_info = (pre_end, pre_profit) | ||
| heapq.heappush(end_profit_heap, (endTime[i], max_profit_use_i)) | ||
| max_profit = max(max_profit, max_profit_use_i) | ||
| if pre_info: | ||
| heapq.heappush(end_profit_heap, pre_info) | ||
| return max_profit | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| # DP | ||
|
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. DP ではなく、メモ付き再帰だと思います。なお「メモ付き再帰」は競技プログラミング用語だと思います。 念のため、ループを使った DP でも書いてみていただけますか?
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. DPとメモ付き再帰は同じものだと思っていました。 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. "メモ付き再帰"で調べたら、競技プログラミングの第3,4世代の用例が多かったので、これはあまり下の世代は使わない用語? 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. 自分は「メモ付き再帰」「メモ化再帰」は phoenixstarhiro か nya3jp のどちらかから聞いた気がします。自分自身は「キャッシュ付き再帰」と呼んでいた気がします。 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番目以降のjobを使った時の最大利益をメモしていく | ||
| class Solution: | ||
| def jobScheduling( | ||
| self, startTime: List[int], endTime: List[int], profit: List[int] | ||
| ) -> int: | ||
| jobs = list(zip(startTime, endTime, profit)) | ||
| jobs.sort() | ||
| max_profit_begin_i = [None] * len(jobs) | ||
|
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_profit_begin_i.append(0) | ||
|
|
||
| def find_max_profit(i): | ||
| if max_profit_begin_i[i] is not None: | ||
| return max_profit_begin_i[i] | ||
| profit_skip_i = find_max_profit(i + 1) | ||
| next_index = bisect.bisect_left(jobs, (jobs[i][1], 0, 0), lo=i + 1) | ||
| profit_use_i = find_max_profit(next_index) + jobs[i][2] | ||
| max_profit_begin_i[i] = max(profit_skip_i, profit_use_i) | ||
| return max_profit_begin_i[i] | ||
|
|
||
| return find_max_profit(0) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| class Solution: | ||
| def jobScheduling( | ||
| self, startTime: List[int], endTime: List[int], profit: List[int] | ||
| ) -> int: | ||
| jobs = list(zip(startTime, endTime, profit)) | ||
| jobs.sort() | ||
|
|
||
| @cache | ||
| def find_max_profit(i): | ||
| if i >= len(jobs): | ||
| return 0 | ||
| profit_skip_i = find_max_profit(i + 1) | ||
| next_index = bisect_left(jobs, (jobs[i][1], 0, 0), lo=i + 1) | ||
| profit_use_i = find_max_profit(next_index) + jobs[i][2] | ||
| return max(profit_skip_i, profit_use_i) | ||
|
|
||
| return find_max_profit(0) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| # DP | ||
| class Solution: | ||
| def jobScheduling( | ||
| self, startTime: List[int], endTime: List[int], profit: List[int] | ||
| ) -> int: | ||
| jobs = list(sorted(zip(startTime, endTime, profit))) | ||
| max_profit = [0] * (len(jobs) + 1) | ||
| for i in range(len(jobs) - 1, -1, -1): | ||
| profit_skip_running_job = max_profit[i + 1] | ||
| next_index = bisect_left(jobs, (jobs[i][1], 0, 0), lo=i + 1) | ||
| profit_use_running_job = max_profit[next_index] + jobs[i][2] | ||
| max_profit[i] = max(profit_skip_running_job, profit_use_running_job) | ||
| return max_profit[0] | ||
|
|
||
|
|
||
| # level_1の書き直し | ||
| class Solution: | ||
| def jobScheduling( | ||
| self, startTime: List[int], endTime: List[int], profit: List[int] | ||
| ) -> int: | ||
| jobs = list(sorted(zip(startTime, endTime, profit))) | ||
| jobs.append((float("inf"), float("inf"), 0)) | ||
| max_profit = 0 | ||
| running_jobs = [] | ||
| for job_start, job_end, job_profit in jobs: | ||
| while running_jobs and running_jobs[0][0] <= job_start: | ||
| _, max_profit_from_running_job = heapq.heappop(running_jobs) | ||
| max_profit = max(max_profit, max_profit_from_running_job) | ||
| heapq.heappush(running_jobs, (job_end, max_profit + job_profit)) | ||
| return max_profit |
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.
自分がこの問題を読んだときに思い付いた解法は、
でした。 level 1~3 の解法は思い浮かびませんでした。
level 1 は、第一感やや複雑に感じました。 level 2~3 は、スマートだと感じました。