洛谷【P1020】关于最长上升子序列的四种解法

这是第一次写博客,参加学校的acm已经有一年的时间了,现在才开始写,说实话有点晚,但再不写就更晚了,现在在做最长子序列方面的题,找了一道比较经典的题来举例。
题目链接:https://www.luogu.org/problemnew/show/P1020
首先讲题目意思,第一问求最长不上升子序列,第二问求最长上升子序列,说实话第二问一开始我没看出是求上升子序列的,看了别的大佬的题解才发现用上升子序列。
第一种,简单的动态规划,O(n^2)的算法

#include <map>
#include <string>
#include <vector>
#include <cstdio>
#include <cmath>
#include <queue>
#include <stack>
#include <cstring>
#include <iostream>
#include <algorithm>
#define INF 0x3f3f3f3f

using namespace std;

int a[100005],dp1[100005],dp2[100005];

int main()
{
    int n=0;
    while(~scanf("%d",&a[++n]))
    continue;
    n--;
    int ans1=0,ans2=0;
    for(int i=n; i>0; i--)
    {
        dp1[i]=1;
        for(int j=i+1; j<=n; j++)
        {
            if(a[j]<=a[i])
            {
                dp1[i]=max(dp1[i],dp1[j]+1);
            }
        }
        ans1=max(ans1,dp1[i]);
    }
    for(int i=1; i<=n; i++)
    {
        dp2[i]=1;
        for(int j=1;j<i;j++)
        {
            if(a[j]<a[i])
            {
                dp2[i]=max(dp2[i],dp2[j]+1);
            }
        }
        ans2=max(ans2,dp2[i]);
    }
    printf("%d\n",ans1);
    printf("%d\n",ans2);
    return 0;
}

第二种,用了二分法,O(n*logn)的算法,我是借鉴了大佬的思想
大佬博客链接:https://www.cnblogs.com/wxjor/p/5524447.html
代码如下:

#include <map>
#include <string>
#include <vector>
#include <cstdio>
#include <cmath>
#include <queue>
#include <stack>
#include <cstring>
#include <iostream>
#include <algorithm>
#define INF 0x3f3f3f3f

using namespace std;

int a[100005],dp[100005];

int main()
{
    int n=0;
    while(~scanf("%d",&a[++n]))
        continue;
    n--;
    int ans1=0,ans2=0;
    dp[0]=INF;
    for(int i=1; i<=n; i++)
    {
        if(dp[ans1]>=a[i])
        {
            ans1++;
            dp[ans1]=a[i];
        }
        else
        {
            int  l=0,r=ans1;
            while(l<r)
            {
                int mid=(l+r)/2;
                if(dp[mid]<a[i])
                {
                    r=mid;
                }
                else
                {
                    l=mid+1;
                }
            }
            if(l!=0)
            dp[l]=a[i];
        }
    }
    memset(dp,0,sizeof(dp));
    for(int i=1; i<=n; i++)
    {
        if(dp[ans2]<a[i])
        {
            ans2++;
            dp[ans2]=a[i];
        }
        else
        {
            int l=0,r=ans2;
            while(l<r)
            {
                int mid=(l+r)/2;
                if(dp[mid]<a[i])
                {
                    l=mid+1;
                }
                else
                {
                    r=mid;
                }
            }
            dp[l]=a[i];
        }
    }
    printf("%d\n",ans1);
    printf("%d\n",ans2);
    return 0;
}

第三种,我只能说stl真好啊,也是O(n*logn)的算法,同样借鉴大佬
这里最重要的是两个函数lower_bound和upper_bound,自己去查,解决最长上升子序列的好东西
其实思路和上一种方法一样,只是好写并且短
代码如下:

#include <map>
#include <string>
#include <vector>
#include <cstdio>
#include <cmath>
#include <queue>
#include <stack>
#include <cstring>
#include <iostream>
#include <algorithm>
#define INF 0x3f3f3f3f

using namespace std;

int a[100005],dp1[100005],dp2[100005];

struct cmp
{
    bool operator()(int x,int y)
    {
        return x>y;
    }
};

int main()
{
    int n=0;
    while(~scanf("%d",&a[++n]))
        continue;
    n--;
    int ans1=1,ans2=1;
    dp1[1]=dp2[1]=a[1];
    for(int i=2; i<=n; i++)
    {
        if(dp1[ans1]>=a[i])
            dp1[++ans1]=a[i];
        else
            dp1[upper_bound(dp1+1,dp1+ans1+1,a[i],cmp())-dp1]=a[i];
        if(dp2[ans2]<a[i])
            dp2[++ans2]=a[i];
        else
            dp2[lower_bound(dp2+1,dp2+ans2+1,a[i])-dp2]=a[i];
    }
    printf("%d\n",ans1);
    printf("%d\n",ans2);
    return 0;
}

第四种,用树状数组写,O(n*logn)的算法
这种方法我就不放代码了,自己写吧

猜你喜欢

转载自blog.csdn.net/PWeiDa/article/details/82226758