最长上升子序列

最长上升子序列问题是各类信息学竞赛中的常见题型,也常常用来做介绍动态规划算法的引例,笔者接下来将会对POJ上出现过的这类题目做一个总结,并介绍解决LIS问题的两个常用
算法(n^2)和(nlogn).
问题描述:给出一个序列a1,a2,a3,a4,a5,a6,a7....an,求它的一个子序列(设为s1,s2,...sn),使得这个子序列满足这样的性质,s1 例如有一个序列:1  7  3  5  9  4  8,它的最长上升子序列就是 1 3 4 8 长度为4.

算法1(n^2):我们依次遍历整个序列,每一次求出从第一个数到当前这个数的最长上升子序列,直至遍历到最后一个数字为止,然后再取dp数组里最大的那个即为整个序列的最长上升子序列。我们用dp[i]来存放序列1-i的最长上升子序列的长度,那么dp[i]=max(dp[j])+1,(j∈[1, i-1]); 显然dp[1]=1,我们从i=2开始遍历后面的元素即可。
下面是模板:


//最长上升子序列(n^2)模板
//入口参数:1.数组名称 2.数组长度(注意从1号位置开始)
template
int LIS(T a[],int n)
{
    int i,j;
    int ans=1;
    int m=0;
    int *dp=new int[n+1];
    dp[1]=1;
    for(i=2;i<=n;i++)
    {
        m=0;
        for(j=1;j         {
            if(dp[j]>m&&a[j]                 m=dp[j];
        }
        dp[i]=m+1;
        if(dp[i]>ans)
            ans=dp[i];
    }
    return ans;
}

算法2(nlogn):维护一个一维数组c,并且这个数组是动态扩展的,初始大小为1,c[i]表示最长上升子序列长度是i的所有子串中末尾最小的那个数,根据这个数字,我们可以比较知道
,只要当前考察的这个数比c[i]大,那么当前这个数一定能通过c[i]构成一个长度为i+1的上升子序列。当然我们希望在C数组中找一个尽量靠后的数字,这样我们得到的上升子串的长度最长,查找的时候使用二分搜索,这样时间复杂度便下降了。
模板如下:

//最长上升子序列nlogn模板
//入口参数:数组名+数组长度,类型不限,结构体类型可以通过重载运算符实现
//数组下标从1号开始。
/**//BEGIN_TEMPLATE_BY_ABILITYTAO_ACM
template
int bsearch(T c[],int n,T a)
{
   
    int l=1, r=n;
    while(l<=r)
    {
        int mid = (l+r)/2;
        if( a > c[mid] && a <= c[mid+1] )
            return mid+1; // >&&<= 换为: >= && <
        else if( a < c[mid] )
           r = mid-1;
        else l = mid+1;
    }
   
}

template
int LIS(T a[], int n)
{
   
    int i, j, size = 1;
    T *c=new T[n+1];
    int *dp=new int[n+1];
    c[1] = a[1]; dp[1] = 1;
   
    for(i=2;i<=n;++i)
    {
        if( a[i] <= c[1] ) j = 1;// <= 换为: <
        else if( a[i] >c[size] )
            j=++size;   // > 换为: >=
        else
            j = bsearch(c, size, a[i]);
        c[j] = a[i]; dp[i] = j;
    }
    return size;
   
}
/**//END_TEMPLATE_BY_ABILITYTAO_ACM

下面是pku上有关LIS的题
PKU 2533 ——Longest Ordered Subsequence 裸LIS,没什么可说的
#include
using namespace std;


int a[2000];

template
int LIS(T a[],int n)
{
    int i,j;
    int ans=1;
    int m=0;
    int *dp=new int[n+1];
    dp[1]=1;
    for(i=2;i<=n;i++)
    {
        m=0;
        for(j=1;j         {
            if(dp[j]>m&&a[j]                 m=dp[j];
        }
        dp[i]=m+1;
        if(dp[i]>ans)
            ans=dp[i];
    }
    return ans;
}

 

 

 

 

int main ()
{
    int n;
    int i;
    int finalmax=0;
    cin>>n;
    for(i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    printf("%d\n" ,LIS(a,n));
    return 0;
}

 


PKU 1631——Bridging signals 这题用N^2算法要超时,必须使用NlogN的算法
#include
#include
using namespace std;


const int N =  40000;
int a[N];
//最长上升子序列nlogn模板
//入口参数:数组名+数组长度,类型不限,结构体类型可以通过重载运算符实现
//数组下标从1号开始。
/**//BEGIN_TEMPLATE_BY_ABILITYTAO_ACM
template
int bsearch(T c[],int n,T a)
{
   
    int l=1, r=n;
    while(l<=r)
    {
        int mid = (l+r)/2;
        if( a > c[mid] && a <= c[mid+1] ) return mid+1; // >&&<= 换为: >= && <
        else if( a < c[mid] ) r = mid-1;
        else l = mid+1;
    }
   
}

template
int LIS(T a[], int n)
{
   
    int i, j, size = 1;
    T *c=new T[n+1];
    int *dp=new int[n+1];
    c[1] = a[1]; dp[1] = 1;
   
    for(i=2;i<=n;++i)
    {
        if( a[i] <= c[1] ) j = 1;// <= 换为: <
        else if( a[i] >c[size] )
            j=++size;   // > 换为: >=
        else
            j = bsearch(c, size, a[i]);
        c[j] = a[i]; dp[i] = j;
    }
    return size;
   
}
/**//END_TEMPLATE_BY_ABILITYTAO_ACM

 

int main()
{
    int testcase;
    int n;
    scanf("%d",&testcase);
    int i,j;
    for(i=1;i<=testcase;i++)
    {
        scanf("%d",&n);
        for(j=1;j<=n;j++)
            scanf("%d",&a[j]);
        printf("%d\n",LIS(a,n));
    }
    return 0;
}


PKU 1887——Testing the CATCHER 最长不升子序列,再次纪念下第一道动归题
Posted by abilitytao at 2008-09-25 00:50:48 on Problem 1887
#include
#include
#include
#include
using namespace std;


int num[10000];
int dp[10000];

int main()
{
    int n;
    int i,j;
    int len;
    int max;
    int finalmax;
    int flag=1;
    while(scanf("%d",&n))
    {
        if(n==-1)
            break;
        num[0]=n;
        for(i=1;;i++)
        {
            scanf("%d",&n);
            if(n==-1)
                break;
            else
                num[i]=n;
        }
        len=i-1;

        dp[len]=1;

        for(i=len-1;i>=0;i--)
        {
            max=0;
            for(j=i+1;j<=len;j++)
            {
                if(dp[j]>max&&num[j]                     max=dp[j];
            }
            dp[i]=max+1;
        }
        finalmax=0;
        for(i=0;i<=len;i++)
        {
            if(dp[i]>finalmax)
                finalmax=dp[i];
        }
        printf("Test #%d:\n  maximum possible interceptions: %d\n\n",flag,finalmax);
        flag++;
    }
    return 0;
}


PKU 1609 Tiling Up Blocks 二维最长不下降子序列
#include
#include
using namespace std;
#define MAX 100001


int dp[MAX];

struct node
{
    int a,b;
}l[MAX];

int cmp(const void *a,const void *b)
{
    struct node c=(*(node*)a);
    struct node d=(*(node*)b);
    if(c.a!=d.a)
        return c.a-d.a;
    else return c.b-d.b;
}

int main()
{
    int n;
    int i,j;
    int maxnum;
    while(scanf("%d",&n))
    {
        maxnum=1;
        if(n==0)
        {
            printf("*\n");
            break;
        }
           
        for(i=1;i<=n;i++)
        {
            scanf("%d%d",&l[i].a,&l[i].b);
        }
        qsort(l+1,n,sizeof(l[1]),cmp);
        dp[1]=1;
        int temp;
        for(i=2;i<=n;i++)
        {
            temp=0;
            for(j=1;j<=i-1;j++)
            {
                if(dp[j]>temp&&l[i].a>=l[j].a&&l[i].b>=l[j].b)
                    temp=dp[j];
            }
            dp[i]=temp+1;
            if(dp[i]>maxnum)
                maxnum=dp[i];
        }
        printf("%d\n",maxnum);
    }
    return 0;
}

你可能感兴趣的:(面试)