最长上升子序列模板(LIS)
题目链接:链接
给定一个长度为 N 的数列,求数值严格单调递增的子序列的长度最长是多少。
输入格式
第一行包含整数 N
第二行包含 N 个整数,表示完整序列。
输出格式
输出一个整数,表示最大长度。
数据范围
1≤N≤1000
−10 ^ 9≤数列中的数≤10 ^ 9
状态表示:
集合:f[i]表示以a[i]结尾的严格单调递增的子序列
属性:max
状态计算:
#include<iostream>
using namespace std;
const int maxn=1010;
int n;
int a[maxn],f[maxn];
int main(){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
int res=0;
for(int i=1;i<=n;i++){
f[i]=1;
for(int j=1;j<i;j++){
if(a[i]>a[j])
f[i]=max(f[i],f[j]+1);
}
res=max(res,f[i]);
}
cout<<res;
return 0;
}
怪盗基德的滑翔翼
正向LIS+反向LIS
#include<iostream>
using namespace std;
const int maxn=110;
int n;
int a[maxn],f[maxn];
int main(){
int t;
cin>>t;
while(t--){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
int res=0;
for(int i=1;i<=n;i++){
f[i]=1;
for(int j=1;j<i;j++){
if(a[i]>a[j])
f[i]=max(f[i],f[j]+1);
}
res=max(res,f[i]);
}
for(int i=n;i>=1;i--){
f[i]=1;
for(int j=n;j>i;j--)
if(a[i]>a[j]) f[i]=max(f[i],f[j]+1);
res=max(res,f[i]);
}
cout<<res<<endl;
}
return 0;
}
登山
正向LIS+反向LIS
#include<iostream>
using namespace std;
int n;
const int maxn=1010;
int a[maxn],f[maxn],g[maxn];
int main(){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++){
f[i]=1;
for(int j=1;j<i;j++){
if(a[i]>a[j]) f[i]=max(f[i],f[j]+1);
}
}
for(int i=n;i>=0;i--){
g[i]=1;
for(int j=n;j>i;j--){
if(a[i]>a[j])g[i]=max(g[i],g[j]+1);
}
}
int res=0;
for(int i=1;i<=n;i++)
res=max(res,f[i]+g[i]-1);
cout<<res;
return 0;
}
友好城市
使用pair排序
下面这个代码只能通过n<=5000的数据范围。
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=5010;
int n;
typedef pair<int ,int> PII;
PII a[maxn];
int f[maxn];
int main(){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i].first>>a[i].second;
sort(a+1,a+n+1);//排序的时候,记得下标!!!
int res=0;
for(int i=1;i<=n;i++){
f[i]=1;
for(int j=1;j<i;j++){
if(a[i].second>a[j].second) f[i]=max(f[i],f[j]+1);
}
res=max(res,f[i]);
}
cout<<res;
return 0;
}
最长上升子序列和
几乎就是板子题
#include<iostream>
using namespace std;
const int maxn=1010;
int n;
int a[maxn];
int f[maxn];
int main(){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
int res=0;
for(int i=1;i<=n;i++){
f[i]=a[i];
for(int j=1;j<i;j++){
if(a[i]>a[j])
f[i]=max(f[i],f[j]+a[i]);//和板子唯一的区别
}
res=max(res,f[i]);
}
cout<<res;
return 0;
}
拦截导弹
思路:用多少个非上升子序列可以把整个序列覆盖==该序列的最长上升子序列(Dilworth定理)
O(n^2)算法
#include<iostream>
using namespace std;
const int maxn=100010;
int n;
int q[maxn];
int f[maxn],g[maxn];
int main(){
while(cin>>q[n]) n++;
int res=0;
for(int i=0;i<n;i++){
f[i]=1;
for(int j=0;j<i;j++)
if(q[i]<=q[j])
f[i]=max(f[i],f[j]+1);
res=max(res,f[i]);
}
cout<<res<<endl;
int cnt=0;
for(int i=0;i<n;i++){
int k=0;
while(k<cnt && q[i]>g[k])k++;
g[k]=q[i];
if(k>=cnt)cnt++;
}
cout<<cnt;
// int ans=0;
// for(int i=0;i<n;i++){
// f[i]=1;
// for(int j=0;j<i;j++)
// if(q[i]>q[j])
// f[i]=max(f[i],f[j]+1);
// ans=max(ans,f[i]);
// }
// cout<<ans;
return 0;
}
O(nlogn)算法
注意:
最长上升子序列用lower_bound()
最长非下降子序列使用upper_bound()
以此类推
#include<iostream>
#include<vector>
#include<algorithm>
#include<functional>
using namespace std;
const int maxn=100010;
int n=0;
int a[maxn],f[maxn];
int main(){
while(cin>>a[n])n++;
vector<int> s,v;
s.push_back(a[0]);
for(int i=1;i<n;i++){
if(a[i]<=s.back())//满足要求则插入
s.push_back(a[i]);
else
*upper_bound(s.begin(),s.end(),a[i],greater<int>())=a[i];
}
cout<<s.size()<<endl;
v.push_back(a[0]);
for(int i=1;i<n;i++){
if(a[i]>v.back())
v.push_back(a[i]);
else
*lower_bound(v.begin(),v.end(),a[i])=a[i];
}
cout<<v.size();
return 0;
}