最长上升子序列(打印输出和求出最长的长度)

Poj 2533

求一段最长的上升子序列的长度(ps不是非降序列,注意,因为这个就要求你找到的后边一项一定大于前边的),在这里我提供两种思路,一种是DP,最普遍的思路,时间复杂度为0(n^2),另外一种是利用到栈的思想和二分,时间复杂度为0(nlgn)相对来说,如果给的数据n越大,那么这个第二种方法的有事就体现出来了,所以我希望读者能够把第二种思路掌握好,方便解题。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
int a[300],dp[500];

int id;
using namespace std;
int main()
{
int n;scanf("%d",&n);
int ans=0;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
memset(dp,0,sizeof dp);

//id=1;
for(int i=1;i<=n;i++)
{
dp[i]=1;
for(int j=i-1;j>=1;j--)
if(a[i]>a[j])
if(dp[j]+1>dp[i])
{
dp[i]=dp[j]+1;

}
if(dp[i]>ans)
{
ans=dp[i];

//id=i;
}
}
cout<<ans<<endl;
}

这个是dp,还有个栈的思想,但是不一定要用到栈:

#include<iostream>
#include<cstdio>
//#include<stack>
int s[10000],t,n;
using namespace std;
int main()
{
int top=0;
scanf("%d",&n);
scanf("%d",&t);
top=0;
s[0]=t;
top++;
for(int i=1;i<n;i++)
{
scanf("%d",&t);
if(t>s[top-1])//如果发现要进去的数比栈顶元素大,直接进栈
{
s[top++]=t;
}
else//否则就这样
{
int left=0,right=top;//使用二分找到这个站里边比temp大的最小的那个数s[left];
while(left<right)
{
int mid=(left+right)>>1;
if(s[mid]<=t)left=mid+1;
else {right=mid;}
}
s[left]=t;
}
}
printf("%d\n",top);//最后结果就是我们需要的这个站的长度
}
//8
//1 5 8 2 4 9 1 10

但是在输出的时候就只能用第一种方法了(dp),打个最简单的比方,如1,5,8,2这几个数,如果按照第二种方法,那么结果就是1,5,8按照顺序进栈,然后2发现比8小,就在站里边找到一个比2大的最小的一个数,那就是5,然后把5替换掉,变成1,2,8,结果就是3,但是真真的序列应该是1,5,8,这个时候最长的序列的个数是一样的,但是吧里边的值给改了,所以这就不能用于输出,下边,我来写个用于dp的输出:这里重点在pre的函数的应用

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<stack>
int a[300],dp[500],pre[500];
using namespace std;
int id;
void printf()
{
    stack<int> s;
    int k=id;
    while(k>=0)
    {
        s.push(a[k]);//利用栈来反向存储以a[id]结尾的数字串,然后依次通过pre数组来读取逆向的最长子序列的成员;
        k=pre[k];
    }
    while(!s.empty())
    {
        cout<<s.top()<<" ";
        s.pop();
    }
}
int main()
{
    int n;scanf("%d",&n);
    int ans=0;
    for(int i=0;i<n;i++){
            scanf("%d",&a[i]);
    }
    memset(dp,0,sizeof dp);
    id=1;
    pre[0]=-1;//初始化为-1
    for(int i=1;i<=n;i++)
    {
        dp[i]=1;
        for(int j=i-1;j>=1;j--)
        if(a[i]>a[j])
            if(dp[j]+1>dp[i])
            {
                dp[i]=dp[j]+1;
                pre[i]=j;//用于记录在i之前的满足以i为结尾的字符串中最长的子序列的前边那些下标
            }
        if(dp[i]>ans)
        {
             ans=dp[i];
             id=i;//记录下满足要求的子序列的最后一个数的下标
        }
    }
    cout<<ans<<endl;
    printf();
}
//8

//1 5 8 2 4 9 1 10

最后希望我的总结对你有所帮助,么么哒,我一开始也是什么都不会,慢慢的只要你努力,那么你就会发现会与不会并没有那一墙之隔,只有那么一念之差,最后一句话,只要你想学,没有什么你是学不会的,真的!加油,ACMer!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值