LIS的三种求解方法


1.  O(n^2)

传统的求解方法 ,思路为dp,状态转移方程为 dp[i]=max( dp[j]+1,1) 

即到目前的i为止,对前面出现的a[j](j<i)进行遍历 ,如果出现了a[i]>a[j]的情况 ,就使用状态转移方程。

转移方程代表了两种可能 ,第一种为第i个元素自己成为一个上升的队列  ,或者是由于前面的a[j]<a[i]  所以在

dp[j]的基础之上形成了dp[i] = dp[j]+1  但前提是a[i]>a[j]

# include <stdio.h>
int a[500];
int dp[500];
int maxx=1;
int n;
void lis(){
    for(int i=0;i<n;i++){
        dp[i]=1;
        for(int j=0;j<i;j++){
            if(a[i]>a[j]){
                if(dp[i]<dp[j]+1){
                    dp[i]=dp[j]+1;
                }
            }
        }
        if(dp[i]>maxx)
            maxx=dp[i];
    }
}
void output(){
    for(int i=n;i>=0;i--){
        if(dp[i]==maxx){
            printf("%d--",a[i]);
            maxx--;
        }
    }
}
int main(){
    while(scanf("%d",&n)!=EOF){
        maxx = 1;
        for(int i=0;i<n;i++){
            scanf("%d",&a[i]);
        }
        lis();
        printf("最大的长度为%d\n",maxx);
        output();
    }
    return 0;
}
2. 第二种方法 LIS+LCS 把原序列与从小到大排序后的序列做LCS(最长公共子序列),就能求出LIS

顺便写了一下快排 还有可以再开一个数组来记录求LCS的路径 来解决输出的问题

# include<stdio.h>
# include<string.h>
int n;
int a[500];
int b[500];
int dp[500][500];
int res[500][500];
void swap(int *a,int i,int j){
    int tmp = a[i];
    a[i] = a[j];
    a[j] = tmp;
}
void qsort(int *a,int left,int right){
    if(left>=right) return ;
    int len = left+1;
    for(int i=left+1;i<=right;i++){
        if(a[i]<a[left]){
            swap(a,i,len);
            len++;
        }
    }
    len--;
    swap(a,left,len);
    qsort(a,left,len-1);
    qsort(a,len+1,right);
}
void LCS(){
    memset(dp,0,sizeof(dp));
    memset(res,0,sizeof(res));
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(a[i-1]==b[j-1])
                {
                    dp[i][j]=dp[i-1][j-1]+1;
                    res[i][j]=1;
                }
            else if(dp[i-1][j]>dp[i][j-1])
                {
                    dp[i][j]=dp[i-1][j];
                    res[i][j]=2;
                }
            else {
                dp[i][j]=dp[i][j-1];
                res[i][j]=3;
            }
        }
    }
}
int main(){
    while(scanf("%d",&n)!=EOF){
        for(int i=0;i<n;i++){
            scanf("%d",&a[i]);
            b[i]=a[i];
        }
        qsort(a,0,n-1);
        printf("最长上升子序列为:");
        for(int i=0;i<n;i++){
            printf("%d  ",a[i]);
        }
        LCS();
        printf("\nLIS的长度为:");
        printf("%d\n",dp[n][n]);
        int i=n,j=n;
        int len=dp[n][n];
        while(len){
            if(res[i][j]==1){
                printf("%d  ",a[i-1]);
                i--;
                j--;
                len--;
            }
            else if(res[i][j]==2){
                i--;
            }else if(res[i][j]==3){
                j--;
            }
        }
    }
    return 0;
}

3.第三种方法 也是最快的方法 ,O(nlogn) dp+二分

思路: dp[i] 所表示的意思为 在如果LIS的长度为i的话 dp[i]所保存的就是长度为i的LIS的末尾数最小的值

但是这个算法有些问题 就是他求LIS的速度是非常快的 但是如果要输出LIS的话 貌似有点困难

# include <stdio.h>
int n;
int a[500];
int dp[500];
int main(){
    while(scanf("%d",&n)!=EOF){
        for(int i=0;i<n;i++){
            scanf("%d",&a[i]);
        }
        dp[1]=a[0];
        int len =1;
        for(int i=1;i<n;i++){
            int left=1;
            int right=len;
            while(left<=right){
                int mid = (left+right)/2;
                if(a[i]<dp[mid]) right= mid-1;
                else left=mid+1;
            }
            dp[left] = a[i];
            if(left>len) len++;
        }
        printf("%d\n",len);
        
    }
    return 0;
}


阅读更多
个人分类: 面试
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭