最长不下降子序列详解

一、什么是最长不下降子序列

最长不下降子序列(Longest Increasing Subsequence,简称LIS)是指在一个序列中,找出一个最长的子序列满足单调递增。其中的单调递增是指数列中后一个数大于前一个数的情况。比如序列[3,1,4,1,5,9,2,6,5,3,5,8,9,7]的最长不下降子序列是[1,2,3,5,8,9]。

二、最长不下降子序列的求解方法

我们介绍两种方法:动态规划和二分查找法。

1. 动态规划法

动态规划的实现是通过维护一个计数数组dp,用来记录以每个元素为结尾的子序列中最长的LIS长度。我们先初始化dp数组为1,因为每个单独的元素的LIS都是1。

int lengthOfLIS_dp(int* nums, int numsSize) {
    if(numsSize <= 1) return numsSize;

    int dp[numsSize];
    for(int i=0; i<numsSize; i++) dp[i] = 1;

    int maxLen = 1;
    for(int i=1; i<numsSize; i++) {
        for(int j=0; j<i; j++) {
            if(nums[j] maxLen) ? dp[i] : maxLen;
    }

    return maxLen;
}

该算法的时间复杂度是O(n^2),空间复杂度是O(n)。

2. 二分查找法

该方法需要维护一个数组tails,其中tails[i]保存长度为i+1的LIS的最小末尾元素。由于tails长度是不断增加的,因此我们需要一个变量size来维护tails的当前有效长度。

每次遍历数组nums,对于每个元素num,我们在tails中寻找第一个大于等于num的位置index,将tails[index]更新为num,如果index等于size,则表明新增了一条长度为size+1的LIS,需要将size加1。

int lengthOfLIS_binarySearch(int* nums, int numsSize) {
    int tails[numsSize];
    int size = 0;
    for(int i=0; i<numsSize; i++) {
        int left = 0, right = size;
        while(left < right) {
            int mid = (left+right)/2;
            if(tails[mid] < nums[i]) left = mid+1;
            else right = mid;
        }
        tails[left] = nums[i];
        if(left == size) size++;
    }

    return size;
}

该算法的时间复杂度是O(nlogn),空间复杂度是O(n)。

三、最长不下降子序列的实际应用

最长不下降子序列在实际应用中有很多使用场景。这里以纸牌游戏为例,介绍一下如何将LIS算法应用到游戏中。

现在有一组扑克牌,每张牌都有一个权重weight,我们将这些牌分为两部分,分别排列成两个序列a和b。每次可以从a或b的顶部取走一张牌,并将该牌放置在一个额外的序列c的顶部。请问当序列a和b遍历完毕后,序列c的最长不下降子序列的长度是多少?

这个问题可以很自然地转化为求解在序列a和b中取出若干个数后,它们的最长不下降子序列的长度。我们将a和b的元素对应相加,得到一个新的序列,然后求新序列的最长不下降子序列长度即可解决问题。

int max(int a, int b) {
    return (a>b) ? a : b;
}

int lengthOfLIS_game(int* a, int aSize, int* b, int bSize) {
    int c[aSize];
    for(int i=0; i<aSize; i++) c[i] = a[i]+b[i];

    int dp[aSize];
    for(int i=0; i<aSize; i++) dp[i] = 1;

    int maxLen = 1;
    for(int i=1; i<aSize; i++) {
        for(int j=0; j<i; j++) {
            if(c[j] < c[i]) {
                dp[i] = max(dp[i], dp[j]+1);
            }
        }
        maxLen = max(maxLen, dp[i]);
    }

    return maxLen;
}

四、结语

本文介绍了最长不下降子序列的定义、求解方法和实际应用。动态规划和二分查找法都是常用的LIS求解方法,它们的时间和空间复杂度有所不同,选择不同的算法取决于具体应用场景。在游戏开发中,LIS算法可以被广泛地应用,为游戏增添了一份趣味和挑战。

原创文章,作者:FISV,如若转载,请注明出处:https://www.506064.com/n/142997.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
FISVFISV
上一篇 2024-10-14 18:48
下一篇 2024-10-14 18:48

相关推荐

  • Python序列的常用操作

    Python序列是程序中的重要工具,在数据分析、机器学习、图像处理等很多领域都有广泛的应用。Python序列分为三种:列表(list)、元组(tuple)和字符串(string)。…

    编程 2025-04-28
  • Python整数序列求和

    本文主要介绍如何使用Python求解整数序列的和,给出了多种方法和示例代码。 一、基本概念 在Python中,整数序列指的是一组整数的集合,可以使用列表(list)或元组(tupl…

    编程 2025-04-27
  • Python求最长公共子串

    在字符串处理中,最长公共子串是一道非常常见的问题,涉及到了字符串处理的基本技巧。本文将以Python为例,详细介绍如何使用动态规划的思想,求解最长公共子串问题。 一、动态规划求最长…

    编程 2025-04-27
  • Python序列最大值的实现方法

    本篇文章主要介绍如何使用Python寻找序列中的最大值,在文章中我们将通过多个方面,详细阐述如何实现。 一、Python内置函数max() 使用Python内置函数max()可以快…

    编程 2025-04-27
  • Python获取互补序列的方法

    本文主要介绍如何使用Python获取DNA序列的互补序列,包含两种不同的方法及其实现代码。 一、使用字符串替换实现 第一种方法是使用Python字符串的替换方法,将每个碱基与其互补…

    编程 2025-04-27
  • 有序序列是什么意思

    在计算机科学中,有序序列是指有一定规律或者条件的元素的集合。 一、何为有序序列 有序序列是一种线性存储模式,通常用链表或数组来实现。与无序序列不同的是,有序序列中的元素是按照一定规…

    编程 2025-04-27
  • 神经网络代码详解

    神经网络作为一种人工智能技术,被广泛应用于语音识别、图像识别、自然语言处理等领域。而神经网络的模型编写,离不开代码。本文将从多个方面详细阐述神经网络模型编写的代码技术。 一、神经网…

    编程 2025-04-25
  • Linux sync详解

    一、sync概述 sync是Linux中一个非常重要的命令,它可以将文件系统缓存中的内容,强制写入磁盘中。在执行sync之前,所有的文件系统更新将不会立即写入磁盘,而是先缓存在内存…

    编程 2025-04-25
  • Python输入输出详解

    一、文件读写 Python中文件的读写操作是必不可少的基本技能之一。读写文件分别使用open()函数中的’r’和’w’参数,读取文件…

    编程 2025-04-25
  • git config user.name的详解

    一、为什么要使用git config user.name? git是一个非常流行的分布式版本控制系统,很多程序员都会用到它。在使用git commit提交代码时,需要记录commi…

    编程 2025-04-25

发表回复

登录后才能评论