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;
}
时间: 2024-10-14 12:01:21

LIS的三种求解方法的相关文章

三种聚类方法的简单实现

聚类是机器学习中的无监督学习方法的重要一种,近来看了周志华老师的机器学习,专门研究了有关于聚类的一章,收获很多,对于其中的算法也动手实现了一下.主要实现的包括比较常见的k均值聚类.密度聚类和层次聚类,这三种聚类方法上原理都不难,算法过程也很清晰明白.有关于原理可以参阅周志华老师的机器学习第九章,这里只做一下代码的实现. 运行环境是Python2.7+numpy,说实话,numpy坑还是挺多的,其实用Matlab可能会更简单. k均值聚类,核心是是不断更新簇样本的质心. #encoding=utf

递归式的三种求解方式

求解递归式对于分冶算法的重要性不言而喻 以下介绍了三种求解递归式的方法 1,代换法: 缺点:代换法主要的缺点在于,对于任何递归式,我们先得去猜其解,对于猜错了同学,如果不幸猜出的结果和正确结果相差太大,虽然可以推导,但是意义不大: 优点:代换法相较于递归树法更为严谨,相较于主定理应用范围更广,主定理只能求解类似于T(n) = aT(n/b)+n/c这种形式的递归式: 下面给出一个递归表达式T(n) = 2T(n/2)+n,求其解: 首先猜一下其解为O(nlgn);那么我们只需要证明T(n)<cn

谈谈vector容器的三种遍历方法

说明:本文仅供学习交流,转载请标明出处,欢迎转载! vector容器是最简单的顺序容器,其使用方法类似于数组,实际上vector的底层实现就是采用动态数组.在编写程序的过程中,常常会变量容器中的元素,那么如何遍历这些元素呢?本文给出三种遍历方法. 方法一:采用下标遍历 由于vector容器就是对一个动态数组的包装,所以在vector容器的内部,重载了[]运算符,函数原型为:reference operator [] (size_type n);所以我们可以采用类似于数组的方式来访问vector容

android开发中监听器的三种实现方法(OnClickListener)

Android开发中监听器的实现有三种方法,对于初学者来说,能够很好地理解这三种方法,将能更好地增进自己对android中监听器的理解. 一.什么是监听器. 监听器是一个存在于View类下的接口,一般以On******Llistener命名,实现该接口需要复写相应的on****(View v)方法(如onClick(View v)). 二.监听器的三种实现方法 (以OnClickListener为例) 方法一:在Activity中定义一个内部类继承监听器接口(这里是OnClickListener

js oop中的三种继承方法

JS OOP 中的三种继承方法: 很多读者关于js opp的继承比较模糊,本文总结了oop中的三种继承方法,以助于读者进行区分. <继承使用一个子类继承另一个父类,子类可以自动拥有父类的属性和方法.(继承的两方,发生在两个类之间)> 一.通过object实现继承 1:定义父类 function Parent(){} 2:定义子类 funtion Son(){} 3:通过原型给Object对象添加一个扩展方法. Object.prototype.customExtend = function(p

Android中常用的三种存储方法浅析

Android中常用的三种存储方法浅析 Android中数据存储有5种方式: [1]使用SharedPreferences存储数据 [2]文件存储数据 [3]SQLite数据库存储数据 [4]使用ContentProvider存储数据 [5]网络存储数据 在这里我只总结了三种我用到过的或即将可能用到的三种存储方法. 一.使用SharedPreferences存储数据 SharedPreferences是Android平台上一个轻量级的存储类,主要是保存一些常用的配置信息比如窗口状态,它的本质是基

Jquery中each的三种遍历方法

Jquery中each的三种遍历方法 $.post("urladdr", { "data" : "data" }, function(data) { $.each(data, function(n,value) { });}); 1.选择器+遍历 $('div').each(function (i){ i就是索引值 this 表示获取遍历每一个dom对象 }); 2.选择器+遍历 $('div').each(function (index,dom

C#使用DataSet Datatable更新数据库的三种实现方法

本文以实例形式讲述了使用DataSet Datatable更新数据库的三种实现方法,包括CommandBuilder 方法.DataAdapter 更新数据源以及使用sql语句更新.分享给大家供大家参考之用.具体方法如下: 一.自动生成命令的条件 CommandBuilder 方法 a)动态指定 SelectCommand 属性 b)利用 CommandBuilder 对象自动生成 DataAdapter 的 DeleteCommand.InsertCommand 和 UpdateCommand

Java中Map的三种遍历方法

Map的三种遍历方法: 1. 使用keySet遍历,while循环: 2. 使用entrySet遍历,while循环: 3. 使用for循环遍历. 告诉您们一个小秘密: (下↓面是测试代码,最爱看代码了,啰嗦再多也没用) 一般人我不告诉他哦. import java.util.*; //0 我的Main界面 public class MapTraverse { public static void main(String[] args) { String[] str = {"I love you