LIS最长上升子序列(线性上的DP)总结

求最长上升子序列问题如下:

       给定n个整数a1,a2,a3……an,按从左到右的顺序选出尽量多的整数,组成一个上升子序列。例如序列1,6,2,3,7,5,可以选出的最长的上升子序列是1,2,3,5;

分析:

     对于线性结构上的dp,我们常常设以某个点结尾能达到的最大/小值;

    那么我们可以设dp[i]为以i结尾的最长上升子序列的长度,当到i+1的时候,如果这一位的数字比前面的某一个数字大,那么以那个数字结尾的dp值就可以加1;我们可以用两个for循环来实现统计i从1到我们所要求的n,这样的时间复杂度是O(n^{2});

另外还有一个复杂度为O(nlogn)的优化方法:

     新建一个low数组,low[i]表示长度为i的LIS结尾元素的最小值,很显然,根据贪心的思想,如果我们想要得到最长的LIS,那么这个结尾的元素越小越好,那么我们只要不断地维护low数组就好;

    对于每一个a[i],如果a[i]大于当前的low[最长]的结尾元素,那我们把它接到后面,然后更新low[i]的值就好了,否则我们就在low数组中找出第一个大于等于a[i]的元素low[j],用a[i]去更新low[j]就好;由于low数组是递增的,我们可以用二分的方法来完成这个过程;

题目一:POJ - 2533

    模板题,给你一串数字序列,让你求出最长上升子序列

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 1005

using namespace std;
int a[maxn];
int low[maxn];//表示长度为i的LIS序列结尾元素的最小值

int main(void)
{
     int n;
     scanf("%d",&n);
     for(int i=1;i<=n;i++)
     {
          scanf("%d",&a[i]);
     }
     low[1]=a[1];
     int ans=1;
     for(int i=2;i<=n;i++)
     {
          if(a[i]>low[ans])
               low[++ans]=a[i];
          else
          {
               int mid=lower_bound(low,low+ans+1,a[i])-low;//返回的是指向位置的迭代器,减去low之后表示位置,也就是LIS的长度
               low[mid]=a[i];
          }
     }
     printf("%d\n",ans);
     return 0;
}

题目二:POJ - 1631 

题目大意:

     就是讲一个芯片公司把一些芯片连错了,对于左右两个芯片,正确的接线不能存在交叉,但是不幸的是线被一个人弄得十分凌乱,出现了很多交叉的情况,芯片公司要把这些线改正确,但是又想尽量达到最小的损失,就是拆掉最少的线,现在给你一种接线情况,问有多少条接线是可以不用被拆掉的。

题目思路:

     从图中可以明显看出,如果线不存在交叉,那么对于左边的芯片来说,他的i号管脚接的芯片管脚号一定不能大于i+1、i+2号……管脚对应的右边管脚号。因此,我们可以根据左边管脚从小到大的顺序连线情况得到一串数字,如图中的情况,左边1号接的是4、2号接的是2……可以得到序列:4 2 6 3 1 5,因为左边的是1 2 3 4 5 6,是递增的,根据前面的分析,想要不交叉的话,这个序列也应该是递增的。那么问题就转化成了在得到的这个序列中求最长的上升子序列。也是一个模板题,代码跟上面的都差不多。

代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#define maxn 40005

using namespace std;
int a[maxn];
int low[maxn];

int main(void)
{
    int t,n;
    scanf("%d",&t);
    while(t--)
    {
        memset(low,0,sizeof(low));
        memset(a,0,sizeof(a));
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
        low[1]=a[1];
        int ans=1;
        for(int i=2;i<=n;i++)
        {
            if(a[i]>low[ans])
                low[++ans]=a[i];
            else
            {
                int mid=lower_bound(low,low+ans+1,a[i])-low;//返回的是指向位置的迭代器,减去low之后表示位置,也就是LIS的长度
                low[mid]=a[i];
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

呼呼

猜你喜欢

转载自blog.csdn.net/destiny1507/article/details/81412965
今日推荐