导弹拦截
时间复杂度O(nlogn)
求最长上升子序列时:
dp[p] ,p代表最长不上升子序列的子序列的编号从1开始增加,根据dw定理,最大的m编号即是最长上升子序列的长度也是最少分成了m组非上升子序列。
#include<bits/stdc++.h>
using namespace std;
int a[100010],dp1[100010],dp2[100010],n;
int main(){
while(cin>>a[++n]);
--n;
//最不上升子序列
int m=0;
for(int i=1;i<=n;++i){
int p=upper_bound(dp1+1,dp1+1+m,a[i],greater<int>())-dp1;
dp1[p]=a[i];
m=max(m,p);
}
cout<<m<<endl;
m=0;
//最长上升子序列
for(int i=1;i<=n;++i){
int p=lower_bound(dp2+1,dp2+1+m,a[i])-dp2;
dp2[p]=a[i];
m=max(m,p);
}
cout<<m<<endl;
return 0;
}
这种写法可以很好解决二维偏序需要记录分组的情况
E2. String Coloring (hard version)
给出一串小写字母,输出划分最少的子序列个数使得每个子序列没有逆序,并输出每个字母所属的子序列编号。
input:
7
abcdedc
output:
3
1 1 1 1 1 2 3
最长非降子序列分组对应求最长下降子序列个数
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
int dp[maxn];
char s[maxn];
int ans[maxn];
int main(){
int n;
scanf("%d",&n);
scanf("%s",s+1);
int m=0;
for(int i=1;s[i];++i){
int p=lower_bound(dp+1,dp+1+m,s[i]-'a'+1,greater<int>())-dp;
dp[p]=s[i]-'a'+1;
m=max(m,p);
ans[i]=p;
}
cout<<m<<endl;
for(int i=1;i<=n;++i){
cout<<ans[i]<<" ";
}
return 0;
}