软考
APP下载

动态规划求最长单调递增子序列

在算法和数据结构中,找到最长单调递增子序列(Longest Increasing Subsequence,LIS)是一个经典的问题。给定一个序列,我们需要找到一个单调递增的子序列,它的长度是所有单调递增子序列中最大的。在本文中,我们将介绍一个动态规划算法来解决这个问题。

1.问题描述

给定一个长度为n的序列a1, a2, a3, ..., an,找到一个最长的单调递增子序列。

举个例子,给定序列 {3,1,4,1,5,9,2,6,5,4},最长单调递增子序列是{1,4,5,6},长度为4。请注意,该序列不唯一,{1,2,5,6},{1,4,5,5}也可以是解决方案。

2.算法思路

(1)定义状态

设dp[i]为以ai这个元素为结尾的最长单调递增子序列的长度。

(2)状态转移方程

对于dp[i],从dp[1]一直到dp[i-1]这些状态中找到一个长度最长的单调递增子序列,并且最后一个元素小于ai,用这个子序列的长度加1,就是dp[i]的候选答案。 遍历dp[1] ~ dp[i-1]中求得的所有候选答案,取其中大于dp[i-1]的最大值,就是dp[i]的值。

如此一来,dp[i]代表了以ai为结尾的最长单调递增子序列的长度。因为dp[i]夹杂了之前所有状态的信息,所以dp[i]也可以看做是全局最长单调递增子序列的长度。

3.算法实现

//输入数组a

//输出最长单调递增子序列的长度

int LIS(int a[], int n)

{

int dp[n+1];

int len = 1; //找到的最长单调递增子序列的长度

dp[1] = a[1];

for (int i = 2; i <= n; ++i)

{

int j = 1;

while (j <= len && dp[j] < a[i]) ++j;

dp[j] = a[i];

if (j > len) len = j;

}

return len;

}

上面这段代码中,dp[i]的值用一个while循环来求得。这个循环的目的是在dp[1] ~ dp[i-1]中求得所有能作为ai后继状态的答案。要求有序,所以需要用二分查找来优化查找的时间复杂度。

4. 时间复杂度

时间复杂度:O(nlogn)

在真正的实践中,由于动态规划的常数比较小,该算法的实际效率较高,可以解决大多数情况下的问题。

5. 总结

本文介绍了一种动态规划算法解决“最长单调递增子序列”的问题。首先通过定义状态dp[i]来表示以ai为结尾的最长单调递增子序列的长度。然后通过转移方程来求得那些转移状态,最后得出最终结果。时间复杂度为O(nlogn),效果不错。

备考资料 免费领取:软件设计师报考指南+考情分析+思维导图等 立即下载
真题演练 精准解析历年真题,助你高效备考! 立即做题
相关阅读
软件设计师题库