【競プロ】最長増加部分列(LIS)
はじめに
今回は 最長増加部分列(LIS) を求めるアルゴリズムについて、お気持ちから実装まで解説したいと思います!
最長増加部分列(LIS)とは
数列に対してその部分列であって増加列であるものを増加部分列といい、増加部分列のうち長さが最大なものを最長部分増加列といいます。
数列に対するその部分列は、順序を変えずに取り出した列(競プロにおいては空列は含まないことが多い)のこと。
例
数列
アルゴリズム
お気持ち
配列のi番目までで作れる増加部分列を考える。
増加部分列のj番目の要素のうちの最小値を記録していく。
具体例
配列A={1, 3, 4, 4, 2, 5}
記録する配列DP
-
DP
をINF
で初期化j 0 1 2 3 4 5 DP INF INF INF INF INF INF -
配列
A
のi=0
までの要素{1}
で増加部分列{1}
が作れるためDP[0]
を更新j 0 1 2 3 4 5 DP 1 INF INF INF INF INF -
配列
A
のi=1
までの要素{1,3}
で増加部分列{1,3}
が作れるためDP[1]
を更新
(増加部分列{3}
も作れるが、DP[0]
を更新できないためj 0 1 2 3 4 5 DP 1 3 INF INF INF INF -
配列
A
のi=2
までの要素{1,3,4}
で増加部分列{1,3,4}
が作れるためDP[2]
を更新j 0 1 2 3 4 5 DP 1 3 4 INF INF INF -
配列
A
のi=3
までの要素{1,3,4,4}
ではDP
は更新できないj 0 1 2 3 4 5 DP[i] 1 3 4 INF INF INF -
配列
A
のi=4
までの要素{1,3,4,4,2}
で増加部分列{1,2}
が作れるためDP[1]
を更新j 0 1 2 3 4 5 DP 1 2 4 INF INF INF -
配列
A
のi=5
までの要素{1,3,4,4,2,5}
で増加部分列{1,3,4,5}
が作れるためDP[3]
を更新j 0 1 2 3 4 5 DP 1 2 4 5 INF INF
DP
の更新の際に、A
のi=kまでの要素の増加部分列を調べている計算量が大きくなってしまう。
具体例のDP
の更新を見ると次のことがわかる。
i=kのとき、
DP
のA[k]
以上のインデックスが最小の要素をA[k]
で更新している
これは、具体例で更新できない場合も含む。
証明
このとき、
の漸化式で求まる。
DP は増加列であること
数列
であり、
DP_{i+1} \neq DP_{i} のとき
したがって、
このとき、
つまり、
このとき、
DP_{i+1} = DP_{i} のとき
上記の漸化式は正しい。
コード
vector<long long> LIS(vector<long long> A) {
long long N = A.size();
long long INF=(1LL<<60);
vector<long long> DP(N, INF);
rep(i,N){
auto itr = lower_bound(DP.begin(),DP.end(), A[i]); // A[i]以上のDPの要素へのイテレータを取得
*itr = A[i]; // イテレータが指す要素をA[i]に更新
}
return DP;
}
参考
Discussion