Day 030-Q3 — 重み付き区間スケジューリング(DP + 二分探索)

2026-05-13 赤色 Master / Phase 8+ ★★★★★★★★★ DP + 二分探索

問題

$N$ 本の仕事 $(s_i, e_i, w_i)$ から重複しない部分集合を選び利益最大化せよ。

制約

$1 \le N \le 2 \times 10^5$
$0 \le s_i < e_i \le 10^9$
$1 \le w_i \le 10^9$

入出力例

入力例 1

5
1 3 50
2 5 60
3 6 70
4 7 80
6 9 90

出力例 1

210

[1,3,50] + [3,6,70] + [6,9,90] = 210

ヒント (段階的開示)

ヒント1: 方向性
終了時刻ソート + DP。
ヒント2: アプローチ
dp[i] = w[i] + max(dp[j] | e[j] <= s[i])、二分探索で前の仕事を $O(\log N)$ 検索。
ヒント3: 実装
bisect_right(ends, s_i, 0, i) - 1 で適切な前仕事を特定。

模範解答 (Python)

import sys
import bisect
input = sys.stdin.readline

def solve():
    N = int(input())
    jobs = []
    for _ in range(N):
        s, e, w = map(int, input().split())
        jobs.append((e, s, w))
    jobs.sort()
    dp = [0] * (N + 1)
    ends = [j[0] for j in jobs]
    for i in range(N):
        e_i, s_i, w_i = jobs[i]
        k = bisect.bisect_right(ends, s_i, 0, i) - 1
        dp_with = dp[k + 1] + w_i
        dp[i + 1] = max(dp[i], dp_with)
    print(dp[N])

solve()

Step-by-Step 解説

1ソート
終了時刻昇順。これで「仕事 $i$ より前に終わったもの」だけが選択候補。
2DP 定義
$dp[i]$ = 最初 $i$ 仕事を検討した最大利益。選ぶ/選ばないの2択。
3二分探索
bisect_right(ends, s_i, 0, i) - 1 で $e[j] \le s_i$ の最大 j を $O(\log N)$。
4遷移
dp[i+1] = max(dp[i], dp[k+1] + w_i)
5出力
dp[N]

よくあるミス

ミス原因正しい書き方
境界条件 ($e_j = s_i$)等号OK/NGの混同等号 OK で重複しないなら e[j] <= s_i
開始時刻でソート順序条件が成り立たない終了時刻でソート
bisect 範囲外hi 未指定hi=i
0/1-indexed 混同dp の管理dp は 1-indexed (dp[0]=0)

次のステップ

  • 最大 $K$ 本選択($K \le 100$)
  • 無制限ナップサック型変形

自己評価

自分の回答

気づき・メモ