終わりの順の貪欲で重なりを消す

区間問題 159

1 台の機械に入った作業の時間帯の並びから、残りが重ならないように外す本数の最少を求める。終わりの早い順に並べ、残せる物を前から選んでいくと、外す本数が最も少なくなります。

海外で定番の問題リスト 4 本には入っていません

問題

1 台の印刷機に入った仕事の時間帯が、[始まり, 終わり] の組の並び jobs で渡されます。印刷機は同時に 1 つの仕事しかできません。いくつかの仕事を外して、残りのどの 2 つも重ならないようにしたいとき、外す仕事の数の最少を返す関数を書いてください。

  • 片方の終わりと、もう片方の始まりが同じ (ちょうど接している) 2 つは、重ならないと見ます
  • jobs は並べ替えられていません
  • どの組も、始まりは終わりより小さいです
  • jobs の長さは 1 以上 100000 以下、値はどれも -50000 以上 50000 以下です

入力と出力の例

例 1

[2, 3] と [4, 7] を残し、[1, 5] と [6, 8] を外せば重なりが消えます。3 つ残す方法はありません。

jobs = [[1, 5], [2, 3], [4, 7], [6, 8]]
2

例 2

どの 2 つも接しているだけなので、外す必要はありません。

jobs = [[3, 5], [5, 9], [0, 3]]
0

例 3

長い [0, 10] 1 つを外せば、短い 3 つが残せます。

jobs = [[0, 10], [1, 2], [3, 4], [5, 6]]
1

面接での聞かれ方

面接では、予定の調整の話として出されることが多いです。

  • 「重なっている会議があります。いくつ取り消せば重ならなくなりますか。最少で答えてください」
  • 「接している会議は重なりますか」と聞き返すと、「重ならないとします」と返ってくるのがふつうです
  • 書き終えたあとに「なぜ終わりの順で選ぶと最少になるのですか」と、理由を聞かれることもあります

「外す最少」は「残せる最多」と同じ問題です。言い換えてから考えると、方針が立てやすくなります。

ヒント

まだ開いていません

付箋は 3 枚。下の段ほど答えに近づきます

  1. ヒント 2

  2. ヒント 3答えに近い

アルゴリズムのチートシートの「配列と文字列」に、この型の要点を表とコードでまとめています。

配列と文字列の問題は、全部の組を調べると手間が大きくなる所を、窓や左右の添字で減らす形が多いです。

main.py
def remove_overlaps(jobs: list[list[int]]) -> int:
    """
    Goal: 何を返すか

    idea1: まず思いつく案
    -> runtime: O(?)  space: O(?)

    idea2: もっと速い案
    -> runtime: O(?)  space: O(?)

    edge case: 空、1 つだけ、など
    """
    # ここに書きます
    pass


# 「実行」を押すと、この行の結果が出ます
print(remove_overlaps([[1, 5], [2, 3], [4, 7], [6, 8]]))

できているか「例で確かめる」を押すと、問題の例を全部この関数に渡して、通ったかをここに出します