POJ 2533 &FJUT 1388最长上升子序列(LIS)

地址 

最长上升子序列

 如果一个数列ai 满足 a1 < a2 < ... < aN 则这个数列被称作上升序列。

给定一个数列a(a1a2, ..., aN)则任意一个数列b(ai1ai2, ..., aiK)并且满足(1 <= i1 < i2 < ... < iK <= N).则b被称为a的子序列。

如果一个数列的子序列是上升序列,则这个序列称为原序列的上升子序列。

 

比如序列(1, 7, 3, 5, 9, 4, 8)的上升子序列有(1, 7), (3, 4, 8)等. 它的最长上升子序列长度是4,即(1, 3, 5, 8).

请你写一个程序求一个序列的最长上升子序列的长度。

Input

第一行是一个整数N表示给定数列的长度. 第二行包括N个范围在0~10000的整数。 1 <= N <= 1000

Output

输出一个整数表示最长上升子序列的最大长度

SampleInput
7
1 7 3 5 9 4 8
SampleOutput
4


方法1:(复杂度O(n²))
思路:dp[i]表示以dp[i]结尾的最长上升子序列
每次都向前找比它小的数和比它大的数的位置,将第一个比它大的替换掉,
前面比他大的可能是a0,a1,a3... ak (0<=k<i)也可能是是空集,空集的话dp【i】=1;
状态转移:dp[ i ] = max { dp [ j ] + 1 ,dp [ i ] } (0 <= j <  i,A[ j ] < A[ i ])
代码:
复制代码
#include<iostream>
#include<algorithm>
using namespace std;
int i,j,n,a[1005],dp[1005],max1;
int main()
{
    cin>>n;
    for(i=0; i<n; i++)
        cin>>a[i];
    dp[0]=1; //初始化,以a[0]结尾的最长递增子序列长度为1
    for(i=1; i<n; i++)
    {
        dp[i]=1;//b[i]最小值为1(空集状态)
        for(j=0; j<i; j++)
            if(a[i]>a[j])
                dp[i]=max(dp[i],dp[j]+1);
                
    }
    for(int i=0;i<n;i++)
    {
        max1=max(max1,dp[i]);
    }

    cout<<max1<<endl;
}
复制代码

方法二:复杂度:O(nlogn)

思路:首先数组a中存输入的数,一个数组b表示所有长度为i的上升子序列中,结尾元素的最小值。

要最长上升子序列最长,末尾的值要尽可能小,因为小的值比大的值更容易创造更长的子序列。显然b数组是递增的

对于每一个a[i],可以通过二分在数组b中查找第一个大于等于a[i]的元素,,就可以用a[i]来代替它,也就是较小的数吧大于等于他的数替换掉了,且每次替换后所得结果不会更少。

 

复制代码
#include<string>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstdio>
#include<cstring>
#include<cstdlib>

#include<map>
using namespace std;
int n;
int a[1005];
int b[1005];
int main()
{
    cin>>n;
    for(int i=0; i<n; i++)
    {
        cin>>a[i];
        b[i]=1e9;

    }

    int len=0;//表示当前长度
    for(int i=0; i<n; i++)
    {
        int j=lower_bound(b,b+n,a[i])-b;//找到属于他的位置
        b[j]=a[i];//替换掉第一个大于或者等于这个数字的那个数
        len=max(len,j+1);//当前下标+1,因为数组从0开始,长度默认为1

    }
    cout<<len<<endl;


    return 0;
}
复制代码

 

 

题1:OpenJudge 4977:怪盗基德的滑翔翼

复制代码
///题意:怪盗基德可以选择其中任意一个建筑作为起点,
///朝一个方向向高度低的建筑物滑翔,求其能经过的建筑物的最大数量
///思路:正向和逆向分别求LIS长度,找出二者中的较大值。
#include<iostream>
#include<algorithm>
using namespace std;
int i,j,n,a[1005],dp[1005],maxx;

int main()
{
    int T;
    cin>>T;

    while(T--)
    {   cin>>n;
    for(i=1;i<=n;i++)cin>>a[i];
          maxx=0;
        //正向求解
        for(i=1; i<=n; i++)
        {
            dp[i]=1;
            for(j=1; j<i; j++)
            {
                if(a[i]>a[j])
                {
                    dp[i]=max(dp[i],dp[j]+1);
                }
            }
            maxx=max(maxx,dp[i]);

        }
        //逆向求解
        for(i=n; i>=1; i--)
        {
            dp[i]=1;
            for(j=n; j>i; j--)
            {
                if(a[i]>a[j])
                {
                    dp[i]=max(dp[i],dp[j]+1);
                }
            }
            maxx=max(maxx,dp[i]);

        }
        cout<<maxx<<endl;
    }


}
View Code 
复制代码

例题2:HDU 1087&FJUT 1379: Super Jumping! Jumping! Jumping!

复制代码
///题意:求最大上升子序列和
///思路:dp[i],表示以a[i]结尾的最大递增子段和
///dp[i]=max(dp[j]+a[i],dp[i])(j<i) dp[j]若为空集则为a[i]

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

int i,j,n,a[1005],b[1005],max1;
int main()
{
    while( cin>>n,n)
    {
        max1=0;
        memset(b,0,sizeof(b));
        for(i=0; i<n; i++)
            cin>>a[i];

        for(i=0; i<n; i++)
        {   b[i]=a[i];
            for(j=0; j<i; j++)
            {
                if(a[i]>a[j])
                    b[i]=max(b[i],b[j]+a[i]);
            }
            max1=max(max1,b[i]);
        }

        cout<<max1<<endl;
    }

}
View Code
复制代码

 例题3:OpenJudge 1996:登山

复制代码
#include<iostream>
#include<algorithm>
#include<cstring>
///题意:1.编号递增:子序列  2.相邻两个景点不能相同 3.下山后不能上山了  2&3:先单调增再单调减的序列的最大值
///思路:正向逆向分别求LIS,最后相加求max(dp1[i],dp2[i])
using namespace std;

int dp1[1005];
int a[1005];
int dp2[1005];
int main()
{   int n;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>a[i];
    }
    for(int i=0;i<n;i++)
    {   dp1[i]=1;
        for(int j=0;j<i;j++)
        {
            if(a[j]<a[i])
            {
                dp1[i]=max(dp1[i],dp1[j]+1);
            }
        }
    }
    for(int i=n-1;i>=0;i--)
    {   dp2[i]=1;
        for(int j=n-1;j>i;j--)
        {
            if(a[j]<a[i])
            {
                dp2[i]=max(dp2[i],dp2[j]+1);
            }
        }
    }
    int ans=0;
    for(int i=0;i<n;i++)
    {
        ans=max(ans,dp1[i]+dp2[i]-1);
    }
    cout<<ans<<endl;
    return 0;
}
View Code
复制代码

例题4:FJUT 2399:合唱队形

 View Code

例题5:洛谷 2782:友好城市

复制代码
/////题意:河两岸有不同的城市,每个城市分别都有且只有一个友好城市,求要在友好城市之间建桥如何建的桥最多且不想交。
//其实就是 有n条直线,求最多可以有多少条直线互不相交。
//思路:将直线的一头看做数组中的i,将另一头看做a[i],每个i对应一个a[i]。
//如果要最多直线且不想交,a[i]的肯定是要递增(如果有不是递增的话就会出现交叉),如a1>=a2&b1>=b2(其中a,b表示两岸);
//所以将一头排序,另一头求最长上升子序列。
//这题洛谷的要用二分才能过。 

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

typedef pair<int,int> PII;
int b[200005];
PII a[200005];
int main()
{
    int n;
    cin>>n;

        for(int i=0; i<n; i++)
        {
            cin>>a[i].first>>a[i].second;
            b[i]=1e9;
        }
        sort(a,a+n);//pair 默认对first升序,当first相同时对second升序;
       int ans=0;
       for(int i=0;i<n;i++)
       {
           int j=lower_bound(b,b+n,a[i].second)-b;
           b[j]=a[i].second;
           ans=max(ans,j+1);

       }
       cout<<ans<<endl;



        return 0;
    }
View Code
复制代码

 例题6:FJUT 1384&HDU 1160:FatMouse's Speed

复制代码
//题意:要求求出体重的升序,速度的降序求出最长的子序列,并从头到尾输出路径。
//思路:结构体排序,按照题目要求排序,然后套LIS,更新的时候记录前缀,最后输出路径用递归或者栈



#include <iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<stack>
using namespace std;const int N = 2005;
int dp[N];
int way[N];
int n;
int i,j,cnt;
stack<int> s;


struct node
{
    int x,y,id;


} a[N];
void print(int pos)
{   
//    if(pos==-1) return ;
//    print(way[pos]);
//    cout<<a[pos].id<<endl;
while(~pos)
{
    s.push(a[pos].id);
    pos=way[pos];
}
while(s.size())
{
    cout<<s.top()<<endl;
    s.pop();
}


}
bool cmp(node a,node b)
{
    if( a.x!=b.x)
    {
        return a.x<b.x;
    }
    return a.y<b.y;
}


int main()
{
    cnt=0;

    while(~scanf("%d%d",&a[cnt].x,&a[cnt].y))
    {
        a[cnt].id=cnt+1;
        cnt++;
    }
    sort(a,a+cnt,cmp);memset(way,-1,sizeof(way));
    int pos=0;
    int len=0;
    for(int i=0; i<cnt; i++)
    {
        dp[i]=1;
        for(int j=0; j<i; j++)
        {
            if(a[i].x>a[j].x&&a[i].y<a[j].y&&dp[i]<dp[j]+1)

            {
                dp[i]=dp[j]+1;
                way[i]=j;
            }
        }
        if(len<dp[i])
        {
            len=dp[i];
            pos=i;

        }


    }
    cout<<len<<endl;
   print(pos);


    return 0;
}
View Code
复制代码

 

 

posted @   blakee  阅读(71)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示