DPL_1_D 最長増加部分列
# 目次
# 問題
http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=DPL_1_D
増加部分列とは、ランダムに並んだ数列の先頭から順に要素が必ず大きくなっていくように要素を選んだ数列のこと.
そのうち、一番長く要素を選ぶことが出来た数列を最長増加部分列(以下、LIS)という.
例えば、 2 5 4 6
という数列であれば、
増加部分列は、
- 2
- 5
- 4
- 6
- 2 5
- 2 4
- 2 6
- 5 6
- 4 6
- 2 5 6
- 2 4 6
となるので、最長は最後の2つなので長さは3.
# 解説
LISを格納する空のコンテナを用意する.
数列を先頭から要素xを取り出して、
- LISが空ならLISにxを加える <-- ベースケース
LIS最後の要素 < x
であればLISの末尾にxを加えるx <= LIS最後の要素
であれば増加列を崩さないぎりぎり最初の要素をxに置き換える
手順3ではLISは昇順にソート済みのはずなので二分探索が適用できる.
"ぎりぎり単調増加を崩さない要素"はC++の場合lower_bound
で簡単に見つけられる.
# 計算量
# 解答1
# 解答2
# 解答1
#define N_MAX 100001
Int N;
vector<Int> A(N_MAX, -1);
vector<Int> L;
Int lis() {
L.push_back(A[0]); // base case
loop(n,1,N) { // general cases
if (last(L) < A[n]) L.push_back(A[n]);
else *lower_bound(span_all(L), A[n]) = A[n];
}
return len(L);
}
int main(void) {
cin >> N;
loop(i,0,N) cin >> A[i];
cout << lis() << endl;
}
# 解答2
こちらのアルゴリズムに利点は無いので使用しないが、後のアイデアのために残しておく.
#define N_MAX 10001
Int N;
vector<Int> A(N_MAX, -1);
vector<Int> L(N_MAX, 0);
Int solve() {
Int k, max_ = 0;
loop(n,1,N+1) {
k = 0;
loop(l,0,n) {
if (A[l] < A[n] && L[k] < L[l]) {
k = l;
}
}
L[n] = L[k] + 1;
if (L[n] > max_) max_ = L[n];
}
return max_;
}
int main(void) {
cin >> N;
loop(i,0,N) cin >> A[i];
cout << solve() << endl;
}