最長増加部分列 (Longest Increasing Subsequence, LIS)
概要
以下のような問題
-
長さ \(N\) の数列がある
-
\(a_{i_1} < a_{i_2} < ... < a_{i_k}\) となる \(i_1 < i_2 < ... < i_k\) の中で \(k\) の最大値を求める
以下のようなDPを考える。
-
\(dp[k] :=\) 長さkとなるLISの中で、列最後の要素の最小値
そして \(dp[j] < a_i\) となる最大のjを二分探索で見つけ、 \(dp[j+1] = \min(a_i, dp[j+1])\) で更新していく。
計算量
\(O(N \log N)\)
実装
from bisect import bisect
# N: 数列の長さ
# A[i]: a_i の値
def solve(N, A):
INF = 10**10
dp = [INF]*(N+1)
dp[0] = -1
for a in A:
idx = bisect(dp, a-1)
dp[idx] = min(a, dp[idx])
return max(i for i in range(N+1) if dp[i] < INF)
Verified
-
AOJ: "DPL_1_D: Combinatorial - Longest Increasing Subsequence": source (Python3, 0.12sec)