【メモ】最長上昇サブシーケンス

4248 ワード

列の数の最も長い上昇するサブシーケンスを求めます


O(n^2)


dp[i]をnum[i]で終わる最長上昇サブシーケンスの長さとして定義し、状態遷移方程式を書くのは難しくない.
dp[i]=max{dp[i],dp[j]+1} (num[j]<num[i] j<i)

できるはずだ.

O(nlogn)


上記の式を見ると,dp[i]は前i−1からdp値が最大のjを選択して更新され,nlognに最適化できることが分かる.
dp[i]を長さiの立ち上がりサブシーケンスの末尾要素の最小値とし、初期化dp値をINFとする.
これによりnum[i]をdp配列に更新し続けることができ,インクリメンタルであるため+O(1)修正を二分検索できる.
参考:『チャレンジプログラミングコンテスト』&&http://www.felix021.com/blog/read.php?1587
テーマ:codevs 3955
コード:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int size=1000010;
const int INF=233333333;

int num[size],dp[size];

int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&num[i]);
        dp[i]=INF;
    }
    for(int i=1;i<=n;i++)
    {
        dp[lower_bound(dp+1,dp+1+n,num[i])-dp]=num[i];
    }
    printf("%d",lower_bound(dp+1,dp+1+n,INF)-dp-1);
    return 0;
}


次のように書くこともできます.
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int size=1000010;
const int INF=233333333;

int num[size],dp[size];

int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&num[i]);
        dp[i]=INF;
    }
    for(int i=1;i<=n;i++)
    {
        *lower_bound(dp+1,dp+1+n,num[i])=num[i];
    }
    printf("%d",lower_bound(dp+1,dp+1+n,INF)-dp-1);
    return 0;
}


参照を解除し、指定したアドレスに保存されている値を変更します.