最长上升子序列 LIS

题目:

http://acm.nyist.net/JudgeOnline/problem.php?pid=17

http://poj.org/problem?id=2533

两道题几乎一样,只不过对于输入输出的要求有所不同罢了。

LIS有两种方法:

一、第一种方法 · 时间复杂度为O(n^2):

状态:dp[i] := 区间为0~i的序列的LIS

转移方程:dp[i] = max(1, dp[k] + 1)  (0<=k<i && s[k]<s[i])

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
using namespace std;

char s[10005];
int dp[10005];

int main ()
{
    int n;
    scanf("%d", &n);
    while(n--) {
        scanf("%s", &s);
        int len = strlen(s);
        for(int i=0; i<len; i++) {
            dp[i] = 1;
            for(int j=0; j<i; j++) {
                if(s[j] < s[i]) {
                    dp[i] = max(dp[i], dp[j]+1);
                }
            }
        }
        int ans = 0;
        for(int i=0; i<len; i++)    ans = max(ans, dp[i]);
        printf("%d\n", ans);
    }

    return 0;
}

 
二、第二种方法 · 时间复杂度为O(nlgn):

这种方法需要用二分查找查询到当前这一位元素应该是长度为多少的上升子序列的最后一位。

状态定义:dp[i] := 长度为i的上升子序列的当前最小的末尾元素,这里之所以要找最小的末尾元素,是因为越小越容易在后面加上一个稍大的元素使得找到的LIS更长!换句话说,倘若我已经将末尾元素的值设置为最低,尽力减小后来者的“门槛”了,但长度仍然只有这么多,那么上升子序列的最长长度也就只能是这样了。

举个例子:序列S为{1,4,2}

step1:将数组dp初始化为{+∞,+∞,+∞}

step2:开始遍历序列S

  ① 当前遍历到的元素为1时,我们在dp序列进行二分查找,寻找第一个大于等于1的元素的下标,我们找到dp[1]=+∞是第一个大于1的元素(dp的下标从1开始),于是dp[1]=1,即:目前来看,长度为1的上升子序列的末尾元素为1

  ② 当前遍历到的元素为4时,在dp序列中找到第一个大于等于4的元素为dp[2]=+∞,所以dp[2]=4。这时,dp={1,4,+∞,+∞},说明长度为1和2的上升子序列的最小末尾元素都已经找到。

  ③ 当前遍历到的元素为2时,在dp序列中找到第一个大于等于2的元素为dp[2]=4,这说明元素2没有办法让目前的最长上升序列更长(除非>4),但是相对于它后面的元素而言,它更有“潜质”作为长度为2的上升子序列的末尾元素,因为它比当前的4要小,4能做到的它都可以,而且可能得到比4更长的上升序列,即做得还可能比4要好。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#define INF 30
using namespace std;

char s[10005];
int dp[10005];

int main ()
{
    int n;
    scanf("%d", &n);
    while(n--) {
        scanf("%s", &s);
        int len = strlen(s);
        for(int i=0; i<len; i++) {
            dp[i] = INF;
        }
        for(int i=0; i<len; i++) {
            *lower_bound(dp, dp+len, s[i]-‘a‘) = s[i]-‘a‘;
        }
        printf("%d\n", lower_bound(dp, dp+len, INF) - dp);
    }

    return 0;
}
时间: 2024-12-21 14:07:40

最长上升子序列 LIS的相关文章

poj1836——dp,最长上升子序列(lis)

poj1836——dp,最长上升子序列(lis) Alignment Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 13767   Accepted: 4450 Description In the army, a platoon is composed by n soldiers. During the morning inspection, the soldiers are aligned in a straight

最长上升子序列LIS模板

1 ///最长上升子序列LIS模板 2 int BinSerch(int l,int r,int cut) 3 { 4 while (l<=r) 5 { 6 int m=(l+r)>>1; 7 if (cut>d[m]&&cut<=d[m+1]) return m; 8 if (cut>d[m]) l=m+1; 9 else r=m-1; 10 } 11 return 0; 12 } 13 14 int LIS(int n) 15 { 16 int le

动态规划(DP),最长递增子序列(LIS)

题目链接:http://poj.org/problem?id=2533 解题报告: 状态转移方程: dp[i]表示以a[i]为结尾的LIS长度 状态转移方程: dp[0]=1; dp[i]=max(dp[k])+1,(k<i),(a[k]<a[i]) #include <stdio.h> #define MAX 1005 int a[MAX];///存数据 int dp[MAX];///dp[i]表示以a[i]为结尾的最长递增子序列(LIS)的长度 int main() { int

最长上升子序列LIS解法(n^n &amp;&amp; nlogn)

最长递增子序列问题 在一列数中寻找一些数满足 任意两个数a[i]和a[j] 若i<j 必有a[i]<a[j] 这样最长的子序列称为最长递增子序列LIS LIS问题有两种常见的解法 一种时间复杂度n^n 一种时间复杂度nlogn 下面我们先来说一下n^n的算法 设dp[i]表示以i结尾的最长上升子序列的长度 把问题分解 分解成序列中每一项最为终点的最大上升子序列 从第二项开始依次判断 最后找出最大的一项就是答案 则状态转移方程为 dp[i] = max{dp[j]+1}, 1<=j<

最长递增子序列 LIS 时间复杂度O(nlogn)的Java实现

关于最长递增子序列时间复杂度O(n^2)的实现方法在博客http://blog.csdn.net/iniegang/article/details/47379873(最长递增子序列 Java实现)中已经做了实现,但是这种方法时间复杂度太高,查阅相关资料后我发现有人提出的算法可以将时间复杂度降低为O(nlogn),这种算法的核心思想就是替换(二分法替换),以下为我对这中算法的理解: 假设随机生成的一个具有10个元素的数组arrayIn[1-10]如[2, 3, 3, 4, 7, 3, 1, 6,

最长上升子序列 (LIS算法(nlong(n)))

设 A[t]表示序列中的第t个数,F[t]表示从1到t这一段中以t结尾的最长上升子序列的长度,初始时设F [t] = 0(t = 1, 2, ..., len(A)).则有动态规划方程:F[t] = max{1, F[j] + 1} (j = 1, 2, ..., t - 1, 且A[j] < A[t]). 现在,我们仔细考虑计算F[t]时的情况.假设有两个元素A[x]和A[y],满足 (1)x < y < t (2)A[x] < A[y] < A[t] (3)F[x] =

hdu1025 dp(最长上升子序列LIS)

题意:有一些穷国和一些富国分别排在两条直线上,每个穷国和一个富国之间可以建道路,但是路不能交叉,给出每个穷国和富国的联系,求最多能建多少条路 我一开始在想有点像二分图匹配orz,很快就发现,当我把穷国按顺序排了之后,富国写在它旁边,能够连接的富国就成了一个上升子序列,那么问题来了!上升子序列最长有多长? 想到了这个之后,代码就码起来吧,最开始我的做法是最土的那种,用 dp[i] 表示以 i 结尾的最长上升子序列的长度,每次对于一个 i 遍历 i 前面的所有数 j ,取小于 i 的所有 j 的最大

nlogn 求最长上升子序列 LIS

最近在做单调队列,发现了最长上升子序列O(nlogn)的求法也有利用单调队列的思想. 最长递增子序列问题:在一列数中寻找一些数,这些数满足:任意两个数a[i]和a[j],若i<j,必有a[i]<a[j],这样最长的子序列称为最长递增子序列. 设dp[i]表示以i为结尾的最长递增子序列的长度,则状态转移方程为: dp[i] = max{dp[j]+1}, 1<=j<i,a[j]<a[i]. 这样简单的复杂度为O(n^2),其实还有更好的方法. 考虑两个数a[x]和a[y],x&

计蒜客课程竞赛入门--最长上升子序列(LIS) 流程记

最长上升子序列 (Longest Increasing Subsequence, 常简称为 LIS) 是动态规划解决的一个经典问题. 我们先讲一下子序列是什么.一个数组的子序列就是从里面选出一些元素,并将他们保持原有的先后顺序排列.比如[1, 2, 3, 4, 5]的子序列有[1, 3, 5].[3, 4],而[1, 5, 3]则不是这个数组的子序列. 这里多介绍一下,还有一个容易与子序列混淆的概念:子串.子串是指从一个数组中选出连续的一个或多个元素,并且保持他们原有的顺序.子串一定是子序列,比

最长上升子序列(LIS)模板

最长递增(上升)子序列问题:在一列数中寻找一些数,这些数满足:任意两个数a[i]和a[j],若i<j,必有a[i]<a[j],这样最长的子序列称为最长递增(上升)子序列. 考虑两个数a[x]和a[y],x>y且a[x]<a[y],且dp[x]=dp[y],当a[t]要选择时,到底取哪一个构成最优的呢?显然选取a[x]更有潜力,因为可能存在a[x]<a[z]<a[y],这样a[t]可以获得更优的值.在这里给我们一个启示,当dp[x]一样时,尽量选择更小的a[x]. 按dp