1. 程式人生 > >洛谷【P1020】關於最長上升子序列的四種解法

洛谷【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; }
#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)的演算法
這種方法我就不放程式碼了,自己寫吧