求解最大升序子序列问题(动态规划)
最大升序子序列:给定数组 [1,7,3,5,9,4,8],则该数组的最大升序子序列为 1,3,5,9,不要求连续。
dp[i]存储第i个位置时,最大升序子序列的长度。
状态转移方程:dp[i] = dp[j] + 1 , dp[j] = max(dp[0,i-1])
初始状态dp[i]=1
求解最大升序子序列长度不像是正规的动态规划问题,每次求解dp[i] 都需要遍历索引 i 之前的数组元素。
具体的,如何求解以 i 结尾的数组的最大升序子序列长度。
遍历arr[ 0 : i ] 内部的数组
若存在 元素 满足arr[j]<arr[i] 且 lis[j]+1 > lis[i] ,
then lis[i] = list[j] +1
def LIS(arr): if not arr or len(arr)==1: return 1 dp = [0 for _ in range(len(arr))] for i in range(len(arr)): dp[i] = 1 for j in range(i): if arr[j] < arr[i] and dp[j]+1>dp[i]: dp[i] = dp[j]+1 return dp[-1]
贪心算法+二分优化 复杂度O(nlog2n)
对于nlog2n的解法,此时的数组dp[i]的含义为以i结尾的最大升序子序列,注意此时的dp是动态更新的。当遍历到数组元素arr[i]时,若arr[i]小于dp[i],则在dp中寻找到arr[i]存放的位置并覆盖。注意到覆盖掉dp中原来的元素并不影响此时dp的长度,即之前找到的最大升序子序列长度依然是dp的长度。但为什么要覆盖掉呢?因为只有覆盖掉那个较大的元素,当后面还有更小且更长的升序序列时,这些序列才会被加到dp当中。(不知道说清楚了没有,没看懂的话直接看例子吧)
找一个容易理解的例子:arr 1 3 2 4 5 2 2 2 。 它的最大升序子序列为 1 2 2 2 2
此时的dp数组存储过程,遍历arr
dp: 1
dp: 1 3 (arr[1]比dp中最后一个元素1大,直接追加)
dp: 1 2 (arr[2]比dp中最后一个元素3小,通过二分法找到arr[2]的位置并覆盖)
dp: 1 2 4
dp: 1 2 4 5
dp: 1 2 2 5 (arr[5]比dp中最后一个元素5小,找到dp中arr[5]的位置并覆盖)
dp: 1 2 2 2
dp: 1 2 2 2 2
1 def LIS(self,arr): 2 n = len(arr) 3 dp = [arr[0]] 4 for i in range(1,n): 5 if arr[i]>=dp[-1]: 6 dp.append(arr[i]) 7 else: 8 l,r = 0,len(dp)-1 9 while l<r: 10 mid = (l+r)//2 11 if dp[mid]>arr[i]: 12 r = mid-1 13 else: 14 l = mid+1 15 dp[l] = arr[i] 16 return len(dp)
附上代码 python
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步