LIS(最长上升子序列)

· · 个人记录

本蒟蒻第一次写题解,请大神们多多关照

附:本此题解参考《信息学奥赛一本通》

(此题解仅为练手,方便大家的查看,其实很鸡肋)

【不耐烦地可以直接看标程我都嫌我唠叨

一、问题介绍

  现有n个不相同的整数组成的序列,记为a[1],a[2],……,a[n]。且a[i]<>b[j](i<>j),若有一个子序列,它的下标顺序是由小到
  大的(即i1<i2<i3……ie),并且子序列中数也是由小到大排列的(即a[i1]<a[i2]<a[i3]……ie),则这个长度为e的子序列就是
  这个长度为n的序列的最长上升子序列。
      例如这个长度为6的序列:18 55 21 24 63 44
      它的最长上升子序列长度为4,是18 21 24 44

注明:子序列和子串的区别

  子串是串的一个连续的部分,而子序列是从序列中去掉任意元素而获得的新序列。也就是说,
  子串的位置必须是连续的,而子序列则可以不连续

二、算法分析

这道题经典的DP,我们先介绍下DP的简单思路:

  动态规划有点类似递归,由后往前进行搜索(当然你也可以从前往后)
  - 把一个大的问题分解成一个个的子问题,直到子问题小到不能再小,最终会得到最小子问题。最小子问题的解显而易见,这样递
    推回去,就可以得到原问题的解。
  我们再举栗子说这题(参考书哈,我比书更唠叨):
  1. 对于a[n]来说,它是序列的第一个数,所以当我们从后向前查找时,只存在长度为1的上升子序列。
  2.我们再往前查找(则此时为a[n-1]),则存在下面两种可能性:
    (1)如果a[n-1]<b[n],则有了长度为2的上升子序列:a[n-1],a[n]。
    (2)如果a[n-1]>b[n],则有了长度为1的上升子序列:a[n-1]/a[n]。
  3.如果搜索到a[i],此时求最长上升子序列的方法就是:
    在我们已经搜索过的数中(a[i+1],a[i+2],……,a[n]),找出一个比b[i]大的数,将它的最长上升子序列后接在后面。
    栗子:a[i]=13;a[i+1]=4;a[i+2]=23……
          那我们就可以将a[i+2]所求的最长上升子序列后接在a[i]后面,假如a[i+2]所求出的最长上升子序列是23 55 103,那我
          们就可将此序列接在后面,得出a[i]的最长上升子序列为13 23 55 103。

三、数据结构(就是照抄书上的数据了)

    为算法上的需要,定义一个整数类型的二维数组a[n][3]
    1.a[i][1]表示第i个数的本值。
    2.a[i][2]表示从i到n的最长上升子序列长度。
    3.a[i][3]表示从i开始,最长上升子序列的下一个位置,若a[i][3]=0则后面没有连接的数。(这
      个就是记录路径留后面输出的,就是伪指针)。

四、样例

(n<=200)

【输入样例】

 14
 13 7 9 16 38 24 37 18 44 19 21 22 63 15

【输出样例】

 8
 7 9 16 18 19 21 22 63

五、上程序(看我瞎扯了多久)

- 小二~给我上书上标程

- 得嘞~~

有bug的地方一定要多多包容啊~~~


#include<bits/stdc++.h>
using namespace std;
int n,i,j,t,k,a[1000][3+1];
int main()
{
    scanf("%d",&n);
    for(i=1;i<=n;i++)
    {
        scanf("%d",&a[i][1]);
        a[i][2]=1;//a[i][3]定义是全局变量,自动清零 
    } 
    for(i=n-1;i>=1;i--)//n在最末尾,肯定只有长度为1的,所以这里忽略不计
    {
        t=0;k=0;
        for(j=i+1;j<=n;j++)//寻找最长的,且能让a[i][1]接在后面的子序列 
        {
            if((a[j][1]>a[i][1]) && (a[j][2]>t))
            {
                t=a[j][2]; //记录目前能让a[i][1]接在后面的子序列的最长长度 
                k=j;//记录下标 ,这个就是伪指针 
            }   
        }
        a[i][2]=t+1;//长度加上它本身 
        a[i][3]=k;//记录路径
    }
    k=1;
    for(i=1;i<=n;i++)//求最长上升子序列的起始位置 
        if(a[i][2]>a[k][2])
            k=i;
    printf("%d\n",a[k][2]);//输出最长不下降子序列长度
    while(k!=0)
    {
        printf("%d ",a[k][1]);
        k=a[k][3];
    } 
    return 0;
}