最长递增子序列的三种解法(LIS)
1.用LCS(求LIS) 时间复杂度O(n^2)
思路:将原序列a排序后产生一个新的序列b,比较a和b最长公共子序 结果就是LIS.
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+5;
int dp[2][N];//滚动数组优化
string a,b;
int LCS(){
int la=a.size(),lb=b.size();
for(int i=1;i<=la;i++)
for(int j=1;j<=lb;j++)
if(a[i-1]==b[j-1])//如果相等直接加1
dp[i%2][j]=dp[(i-1)%2][j-1]+1;
else dp[i%2][j]=max(dp[i%2][j-1],dp[(i-1)%2][j]);//否则比较一下
return dp[la%2][lb];
}
int main(){
cin>>a;
b=a;
sort(b.begin(),b.end());//b为排好序递增序列
cout<<LCS()<<endl;
return 0;
}
2.DP(时间复杂度 O(n^2)
思路:设dp[ i ] 为以a[i] 结尾的最长递增子序列, 对每个 i j从 1 到 i-1 遍历 找到最大值。
即 dp [ i ] = max( dp[ j ] , 0 ) +1 . 详情见代码。
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+5;
int dp[N],a[N],ans=1,n;//dp[i]表示以第i个数结尾的LIS的长度
int lis(){ //时间复杂度O(n^2)
dp[1]=1;//初始化
for(int i=2;i<=n;i++)
{
int mx=0;//每次都为0
for(int j=1;j<i;j++)
if(a[j]<a[i]&&dp[j]>mx)//如果a[j]<a[i]且dp[j]>mx 更新mx
mx=dp[j];
dp[i]=mx+1;//更新dp[i]
ans=max(ans,dp[i]);//得到ans
}
return ans;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
cout<<lis()<<endl;
return 0;
}
3.辅助数组模拟.时间复杂度O(nlogn)
思路:用一个辅助数组保存LIS的每个数。对辅助数组的数进行两种操作,一:如果大于末尾的数直接添加,二:找到数组中第一个大于它的数进行替换。详情见代码。
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+5;
int a[N],d[N],n; //d[i]保存LIS的数字
int LIS(){
int l=1;// l表示d[i]的长度
d[1]=a[1];//初始化
for(int i=2;i<=n;i++)
{
if(a[i]>d[l]) //如果大于d[l] 直接添加的末尾
d[++l]=a[i];
else { //否则查找第一个大于a[i]的数 替换掉
int p=lower_bound(d+1,d+l+1,a[i])-d; //该数位置 复杂度 logn
d[p]=a[i];
}
}
return l;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
cout<<LIS()<<endl;
return 0;
}
练习题。
1.导弹拦截
题目传送门本题是利用C++STL的查找功能 求LIS的裸题 复杂度 O(nlogn)
详情见代码。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int a[N],d1[N],d2[N],l1,l2,k;//d2[i]保存 最长不增子序列 d1[i]保存 LIS(严格递增)
void fun(){
l1=l2=1;
d1[1]=d2[1]=a[1];
for(int i=2;i<k;i++)
{
if(a[i]>d1[l1]) //这里必须是大于,不然两个数可以合并为一个拦截系统。
d1[++l1]=a[i];
else {
int p=lower_bound(d1+1,d1+l1+1,a[i])-d1;
d1[p]=a[i];
}
if(a[i]<=d2[l2])
d2[++l2]=a[i];
else { //因为查找第一个‘小于’ 而不是小于等于 所以要用upper_bound
int p=upper_bound(d2+1,d2+l2+1,a[i],greater<int>())-d2; //从左到右找到第一个小于a[i]的数.
d2[p]=a[i];
}
}
}
int main(){
k=1;
while(cin>>a[k]) k++; //这里不能debug了只能到文件结束才会有输出
fun();
printf("%d %d\n",l2,l1);
return 0;
}