标签:
(LIS Longest Increasing Subsequence)给定一个数列,从中删掉任意若干项剩余的序列叫做它的一个子序列,求它的最长的子序列,满足子序列中的元素是单调递增的。
例如给定序列{1,6,3,5,4},答案是3,因为{1,3,4}和{1,5,4}就是长度最长的两个单增子序列。
处看此题,怎么做? 万能的枚举?枚举全部2^n个子序列,找出最长的,固然可以,就是复杂度太高。我们为什么要枚举呢?因为要知道取了哪些数,其实我们只需要考虑上一个数和取了几个数就可以了吧?因为单增的意思是比前一个数大,我们要加入这个数的时候,只考虑它比之前加入的最后一个数大就可以了。而最长的意思是数的个数最多,我们只要知道数的总个数就可以了,没必要知道具体有哪些数。
让我们尝试一下用动态规划的思考办法。首先设置数列是a1, a2, a3…an,为了方便我们加入一项a0=-∞,后面我们将发现这会给我们带来极大的方便。int f[i]表示以第i个数结尾的最长单调子序列的长度, 那么我们看一下加入ai之前的最后一个数是aj,显然j < i并且aj < ai,我们有f(i) = f(j) + 1,因为往后面延长了一项嘛。那根据这个式子,我们显然应该选择最大的f(j),才能让f(i)最大。
于是我们有了递推关系f(i) = max{f(j)| j < i并且aj < ai} + 1,光有了递推关系还不够,初值呢? f(0) = 0,并且我们加入了a0=-∞,这样对每个i > 0,j总是存在的,大不了就达到下标0了嘛。
f[0] = 0; for i = 1 to n do f[i] = 0; for j = 0 to i – 1 do f[i] = max(f[i], f[j] + 1) endfor endfor
第1行:1个数N,N为序列的长度(2 <= N <= 50000) 第2 - N + 1行:每行1个数,对应序列的元素(-10^9 <= S[i] <= 10^9)
输出最长递增子序列的长度。
8 5 1 6 8 2 4 5 10
5
1 def find(y,l,r): 2 if (l==r) and (f[l]<y): 3 return l 4 elif l>r: 5 return l-1 6 else: 7 mid=int((l+r) / 2) 8 if f[mid]>=y: 9 return find(y,l,mid-1) 10 else: 11 return find(y,mid+1,r) 12 n=int(input()) 13 a=[-10000000000] 14 f=[-10000000000] 15 for i in range(n): 16 x=int(input()) 17 a.append(x) 18 f.append(0) 19 m=0 20 for i in range(1,n+1): 21 x=find(a[i],0,m) 22 f[x+1]=a[i] 23 if x==m: 24 m+=1 25 26 print(m)
注意:
这里的二分查找要留心,如果写成这样会死循环:
1 def find(y,l,r): 2 if (l==r) and (f[l]<y): 3 return l 4 else: 5 mid=int((l+r) / 2) 6 if f[mid]>=y: 7 return find(y,l,mid-1) 8 else: 9 return find(y,mid,r)
因为,如果r= l+1且f[l]<y 则反复find(y,l,r)
标签:
原文地址:http://www.cnblogs.com/nbalive2001/p/4773131.html