hdu 5748(求解最長上升子序列的兩種O(nlogn)姿勢)


原題鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=5748


樹狀數組:

/*
對於普通的LIS:
for(i):1~n LIS[i]=1;
if j<i and a[j]<a[i]
LIS[i]=LIS[j]+1
因此可知LIS轉移需要兩個條件
1.(j<i) 序號必須在i之前
2.(a[i]>a[j]) 值必須比a[i]小
利用樹狀數組的順序操作:{查找的都是已經出現的,序號在前(滿足條件1)}
對於每一個值,查找它在數組中的排名,再去尋找小於它的排名的最大的LIS(滿足條件2)
這里利用到了排名,因為這樣可以最大限度地壓縮C數組的空間
*/
#include <bits/stdc++.h>
using namespace std;
const int Max=1e5+10;
int A[Max],V[Max],L[Max],C[Max],len;
int lowbit(int x) {return x&(-x);}
int Sum(int x)           //求值小於等於x的LIS的最大值
{
    int ret=0;
    while(x>0)
    {
        if(C[x]>ret) ret=C[x];
        x-=lowbit(x);
    }
    return ret;
}
void Add(int x,int d)   //值大於等於x的LIS都改為LIS(x)
{
    while(x<=len)
    {
        if(d>C[x]) C[x]=d;
        x+=lowbit(x);
    }
}
int main()
{
    int T;
    for(scanf("%d",&T);T;T--)
    {
       int n;
       scanf("%d",&n);
       for(int i=1;i<=n;i++)
       {
          scanf("%d",&A[i]);
          V[i]=A[i];
       }
       sort(V+1,V+1+n);
       len=unique(V+1,V+1+n)-(V+1);
       memset(C,0,sizeof(C));
       int ans=1,tmp,xu;
       for(int i=1;i<=n;i++)
       {
           xu=lower_bound(V+1,V+1+len,A[i])-(V);
           tmp=Sum(xu-1)+1;
           L[i]=tmp;
           Add(xu,tmp);
       }
       for(int i=1;i<=n;i++)
       {
          if(i!=1) printf(" ");
          printf("%d",L[i]);
       }
       puts("");
    }
    return 0;
}


dp+二分

/*
以dp[x]代表長度為x的LIS,且dp[x]==LIS長度為x的末尾值
每次都往前取dp[x]中最小的一個,當然在保證x盡可能地大的情況下
因為dp[x]是遞增的,所以可以二分,l=1,r=當前最長的LIS
求得當前以小於當前a[i]的最長LIS
*/
#include <bits/stdc++.h>
using namespace std;
const int Max=1e5+10;
int A[Max];
int dp[Max];
int LIS[Max];
void Get_lis(int n)
{
    int i,j,l,r,mid,ans;
    dp[1]=A[1];
    int len=1;
    for(i=2;i<=n;i++)
    {
        if(dp[len]<A[i]) j=++len;
        else
        {
            l=1;r=len;
            ans=0;
            while(l<=r)
            {
                mid=(l+r)>>1;
                if(A[i]>dp[mid]&&A[i]<=dp[mid+1])
                {
                    ans=mid;break;
                }
                else if(A[i]>dp[mid]) l=mid+1;
                else r=mid-1;
            }
            j=ans+1;
        }
        dp[j]=A[i];
        LIS[i]=j;
    }
}
int main()
{
    int T;
    for(scanf("%d",&T);T;T--)
    {
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&A[i]);
            dp[i]=0;
        }
        LIS[1]=1;
        Get_lis(n);
        for(int i=1;i<=n;i++)
        {
            if(i!=1) printf(" ");
            printf("%d",LIS[i]);
        }
        puts("");
    }
    return 0;
}


其實還有一種單調隊列求最長上升子序列的方法,可是不能用來解這道題

/*
無解。。。
單調隊列只能求出總體的LIS長度
*/
#include <bits/stdc++.h>
using namespace std;
const int Max=1e5+10;
int que[Max];
int main()
{
    int T;
    for(scanf("%d",&T);T;T--)
    {
        int n,x,top=0;
        scanf("%d",&n);
        for(int i=0;i<n;i++)
        {
            scanf("%d",&x);
            if(x>que[top]||top==0)
            {
                que[++top]=x;
            }
            else
            {
                int l=1,r=top,mid,ans;
                ans=0;
                while(l<=r)
                {
                    mid=l+(r-l)/2;
                    if(que[mid]<x) l=mid+1;
                    else r=mid-1,ans=mid;
                }
                que[ans]=x;
            }
        }
        cout<<top<<endl;
    }
    return 0;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM