DP的学习计划,刷 https://codeforces.com/problemset?order=BY_RATING_ASC&tags=dp
遇到了这道题 https://codeforces.com/problemset/problem/702/A
以为是最长上升子序列(Longest Increasomg Subsequence)的模板题,发现自己不会做
记录一下大概的思路:
(O(n^2)) 的算法:
- (L[i]) 选择 (A[i]) 为结尾的LIS的长度
- (P[i]) 选择 (A[i]) 为结尾的LIS的倒数第二个元素的Position(可以用来复现整个LIS)
- 临时变量 (k) ,表示 (A[i]) 计算时找到的前面最长的LIS的位置
- 对于每个 (A[i]) ,遍历它前面的每个 (A[j]) ,记录所有满足 (A[j]<A[i]) (可以把 (A[i]) 接在后面)且 (L[j]>L[k]) (更长的子序列)
- (L[i]=L[k]+1)
- (P[i]=k)
(O(nlogn)) 的算法:
- (L[i]) 长度为(i+1) 的IS的末位元素的最小值
- (length) 前 (i) 个元素构成的LIS的长度
- 升序遍历 (i),故 (length) 非降,而且构造的过程中 (L) 数组是递增的
- 如果 (L[length-1]<A[i]) ( (A[i]) 可以接在当前最长的(长度为 (length) 的)LIS后面),则 (L[length++]=A[i])
- 否则在已确定的 (L[0]~L[length]) 中(长度为 (1) 到 (length) 闭区间的LIS的最末元素)二分找到能更新的位置并更新 $*lower\_bound(L,L+length,A[i])=A[i]$
#include<bits/stdc++.h> using namespace std; #define ll long long int A[100005]; int L[100005]; int lis(){ L[0]=A[0]; int length=1; for(int i=1;i<n;i++){ if(L[length-1]<A[i]){ L[length++]=A[i]; } else{ *lower_bound(L,L+length,A[i])=A[i]; //1 7 2 5,已有序列1 7,lower_bound(2)得到7,可以把长度为2的LIS的末尾换成2 } } return length; } int main(){ int n; scanf("%d",&n); for(int i=0;i<n;i++){ scanf("%d",&A[i]); } printf("%d ",lis()); }
复原的时候也很简单,把$L[0]~L[length-1]$全部输出就可以了
但是最后我发现这个702A并不是LIS,因为他不是子序列而是子串!
2019-01-16