最长上升子序列(最长递增子序列,LIS)
长度问题
给定长度为nnn的序列vvv,求此序列中严格递增(上升)的子序列长度最大值(子序列可由原序列中不连续的元素构成)
朴素DP(O(n2)O(n^2)O(n2))
闫氏DP分析法
-
状态表示:
- 集合dpdpdp:所有满足递增条件的元素集
- 属性:MaxMaxMax,dp[i]dp[i]dp[i]表示以iii结尾的最长递增子序列长度,init(dp)=1init(dp)=1init(dp)=1
-
状态计算:
- iii为当前工作区间尾指针,jjj为当前工作区间工作指针
- iii不可选:v[i]≤v[j]v[i]\le v[j]v[i]≤v[j],不满足递增条件,不选
- iii可选:v[i]>v[j]v[i]>v[j]v[i]>v[j]
- 选iii:dp[i]dp[i]dp[i]长度继承自dp[j]dp[j]dp[j],dp[i]=dp[j]+1dp[i]=dp[j]+1dp[i]=dp[j]+1
- 不选iii:该子序列[[1],[...],[j]][[1],[...],[j]][[1],[...],[j]]可能是一个非最优子序列或最优子序列的子序列,dp[i]=dp[i]dp[i]=dp[i]dp[i]=dp[i]
-
转移方程式:dp[i]=max(dp[i],dp[j]+1)dp[i]=max(dp[i],dp[j]+1)dp[i]=max(dp[i],dp[j]+1)
extern vector<int>v,dp;
int lis(){
fill(dp.begin(),dp.end(),1);
for(int i=0;i<v.size();i++)
for(int j=0;j<i;j++)
if(v[i]>v[j])//v[i]可选
dp[i]=max(dp[i],dp[j]+1);
return *max_element(dp.begin(),dp.end());
}
贪心(O(nlog2nn\log_2nnlog2n))
思路:设原序列vvv,答案序列ansansans,当前工作指针为iii。初始化ans[0]=v[0]ans[0]=v[0]ans[0]=v[0],遍历原序列vvv:
- 若v[i]v[i]v[i]>ans.back()ans.back()ans.back(),则将v[i]v[i]v[i]加入ansansans末尾
- 否则,用v[i]v[i]v[i]替换ansansans中首个≥v[i]\ge v[i]≥v[i]的元素。由于ansansans始终有序,故可采用二分加速
extern int n;
extern vector<int>v,ans;
void lis(){
ans.push_back(v[0]);
for(auto i:v){
if(i>ans.back()]) ans.push_back(i);
else ans[distance(ans.begin(),lower_bound(ans.begin(),ans.end(),i))]=i;
}
cout<<ans.size()<<endl;
}
LCS求解LIS(O(n2)O(n^2)O(n2),不常用)
思路:将原序列vvv排序得到序列v′v'v′,两序列的LCSLCSLCS也为有序,即为原序列vvv的LISLISLIS。此方法存在缺陷,仅适用于原序列vvv中不存在重复元素的情况,否则会出现错误。下面仅以二维dpdpdp数组的LCSLCSLCS举例
extern int n,v1[MAX],v2[MAX],dp[MAX][MAX];
void lcs(){
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(v1[i-1]==v2[j-1]) dp[i][j]=dp[i-1][j-1]+1;
else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
cout<<dp[n][n]<<endl;
}
最长连续上升子序列
转移方程式:dp[i]=dp[i−1]+1dp[i]=dp[i-1]+1dp[i]=dp[i−1]+1
extern vector<int>v,dp;
void lcis(){
fill(dp.begin(),dp.end(),1);
for(int i=1;i<v.size();i++)
if(v[i]>v[i-1])
dp[i]=dp[i-1]+1;
return *max_element(dp.begin(),dp.end());
}
复杂度O(n)O(n)O(n)