最长上升子序列

示例题目:POJ2533
参考书籍:《挑战程序设计竞赛》

第一种方法:O(n^2)
dp[i]:以a[i]结尾的最长上升子序列的长度

  • 只包含a[i]的序列
  • 由a[j]追加a[i]得到(满足j< i并且a[j]< a[i])

状态转移方程:
dp[i]=max{dp[i],dp[j]+1}

#include
#include
#include
#include
using namespace std;
int a[1005];
int dp[1005];

int main()
{
    ios::sync_with_stdio(false);
    int n;
    while(cin>>n)
    {
        for(int i=1;i<=n;i++)
        {
            cin>>a[i];
        }
        memset(dp,0,sizeof(dp));
        int res=0;
        for(int i=1;i<=n;i++)
        {
            dp[i]=1;
            for(int j=1;jif(a[i]>a[j])
                    dp[i]=max(dp[i],dp[j]+1);
            }
            res=max(dp[i],res);
        }
        cout<return 0;
}

第二种方法:O(nlogn)
dp[i]:长度为i+1的上升子序列中末尾元素的最小值(不存在就是INF)
状态转移方程:
对数列从左往右扫一遍,对于当前扫到的元素a[j]
如果i=0或者dp[i-1]< a[j],就用dp[i]=min(dp[i],a[j])更新,
考虑到dp数列中除INF外是单调递增的,dp[i]的指针可由
lower_bound(dp,dp+n,a[j])得出。

#include
#include
#include
using namespace std;
const int INF=0x3f3f3f3f;
int a[1005];
int dp[1005];
/// dp[i]表示长度为i+1的上升子序列中末尾元素的最小值
/// 不存在就是INF

int main()
{
    //int t[5]={1,2,4,4,5};
    //cout<
    ios::sync_with_stdio(false);
    int n;
    while(cin>>n)
    {
        for(int i=0;icin>>a[i];
        }
        fill(dp,dp+n,INF);
        for(int i=0;icout<return 0;
}

按照白书上的理解

#include
#include
#include
using namespace std;
const int INF=0x3f3f3f3f;
int a[1005];
int dp[1005];//dp[i]:以a[i]结尾的LIS的长度
int g[1005];//g[k]=a[j]   (dp[j]=k&&j最小)
///g[1]<=g[2]<=g[3]<=...<=g[n]  (if not exist g[i]=INF)
///dp[i]=max(0,dp[j])+1   (j

int main()
{
    int n;
    while(cin>>n)
    {
        for(int i=0;icin>>a[i];
        fill(g,g+n+1,INF);
        for(int i=0;i//g[k']
            int k=lower_bound(g+1,g+n+1,a[i])-g;
            //g[k']=a[j']
            //g[k]=a[j]>=a[i]
            dp[i]=k;
            //dp[i]=max(0,dp[j'])+1=k  (j'
            g[k]=a[i];
        }
        cout<1,g+n+1,INF)-(g+1)<return 0;
}

你可能感兴趣的:(#,动态规划(dp))