最长上升子序列(最长递增子序列,LIS)
朴素dp(O( n 2 n^2 n2))
- 状态表示:
- 集合 d p [ ] dp[] dp[]:所有满足上升条件的元素集
- 属性:cnt, d p [ i ] dp[i] dp[i]表示以i结尾的上升子序列长度, i n i t ( d p [ ] ) = 1 init(dp[])=1 init(dp[])=1
- 状态计算:
- i i i为当前工作区间尾指针, j j j为当前工作区间工作指针
- 不选 i : a [ j ] > a [ i ] i:a[j]>a[i] i:a[j]>a[i],无法满足最优解,不选
- 选 i : a [ j ] < a [ i i:a[j]<a[i i:a[j]<a[i],满足上升条件,可选。 d p [ i ] dp[i] dp[i]的结果转移自 d p [ j ] dp[j] dp[j],因此转移方程为 d p [ i ] = m a x ( d p [ i ] , d p [ j ] + 1 ) dp[i]=max(dp[i],dp[j]+1) dp[i]=max(dp[i],dp[j]+1)。当 d p [ i ] = d p [ j ] + 1 dp[i]=dp[j]+1 dp[i]=dp[j]+1时,说明 a [ i ] a[i] a[i]已选,否则证明 a [ j ] a[j] a[j]包含在之前已选的子集中
extern vector<int>v(n),dp(n,1);
int lis(){
for(int i=0;i<n;i++)
for(int j=0;j<i;j++)
if(v[j]<v[i])//选v[i]
dp[i]=max(dp[i],dp[j]+1);
return *max_element(dp.begin(),dp.end());
}
LCS求解LIS(O( n 2 n^2 n2))
思路:将序列排序,两序列的LCS即为原序列的LIS
贪心(O( n log n n\log n nlogn))
思路:设原序列 v [ ] v[] v[],答案序列 a n s [ ] ans[] ans[],当前工作指针为 i i i。初始化 a n s [ 0 ] = v [ 0 ] ans[0]=v[0] ans[0]=v[0],遍历原序列 v [ ] v[] v[]:
- 若 v [ i ] v[i] v[i]> a n s [ a n s . s i z e ( ) − 1 ] ans[ans.size()-1] ans[ans.size()−1],则将 v [ i ] v[i] v[i]加入 a n s [ ] ans[] ans[]末尾
- 否则,用 v [ i ] v[i] v[i]替换 a n s [ ] ans[] ans[]中首个 ≥ v [ i ] \ge v[i] ≥v[i]的元素。由于 a n s [ ] ans[] ans[]始终有序,故可采用二分加速
extern int n;
extern vector<int>v,ans;
void lis(){
ans.push_back(v[0]);
for(auto i:v){
if(i>ans[ans.size()-1]) ans.push_back(i);
else ans[distance(ans.begin(),lower_bound(ans.begin(),ans.end(),i))]=i;
}
cout<<ans.size()<<endl;
}