CF568E Longest Increasing Subsequence
CF568E Longest Increasing Subsequence
一道浪费了我一整个晚自习+半个上午的题,不写篇博客简直对不起自己。
其实想清楚思路后实现很简单,还是代码能力太差——写出了n个bug。
• 每个数只能使用一次实际上是个废话。因为在严格的LIS中,同一个数最多只会出现一次
• 首先考虑求答案。考虑LIS问题的其中一个\(O(nlogn)\)做法,即在dp的过程中维护一个数组G表示长度为x的LIS结尾最小的数是多少
• 当每次加入一个确定的数,就和正常求LIS一样做
• 每次加入一个空位,就枚举每一个可能的数,然后转化为确定的数。
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define ri register int
using namespace std;
int read(){
int res=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-f;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
res=(res<<1)+(res<<3)+ch-'0';
ch=getchar();
}
return res*f;
}
const int N=1e5+5;
int a[N],b[N],f[N],x[N],pre[N],pos[N],low[N],len;
bool use[N];
int main(){
int i,j,k,l,n,m;
// freopen();
// freopen();
n=read();
for(i=1;i<=n;i++) a[i]=read();
m=read();
for(i=1;i<=m;i++) b[i]=read();
sort(b+1,b+1+m);
for(i=1;i<=n;i++){
if(a[i]!=-1){
if(a[i]>low[len]){
low[++len]=a[i];pos[len]=i;
f[i]=len;pre[i]=pos[len-1];
continue;
}
k=lower_bound(low+1,low+1+len,a[i])-low-1; //error 1
low[k+1]=a[i];pos[k+1]=i;
f[i]=k+1;pre[i]=pos[k];
}
else{
for(j=m,k=len;j;j--){ //error 2
while(low[k]>=b[j]) k--;
low[k+1]=b[j];pos[k+1]=i;
}
if(low[len+1]) len++;
}
}
k=pos[len];j=m;
if(a[k]==-1) x[k]=b[m],j--; //error 3
for(;len;len--){ //error 4
if(a[k]==-1){
for(i=1;i<k;i++)
if(f[i]==len-1&&a[i]<x[k]&&a[i]!=-1) //error 5
break;
if(i<k) k=i;
else{
while(b[j]>=x[k]&&j) j--;
for(i=k-1;i;i--) if(a[i]==-1) break;
x[i]=b[j];use[j]=1;
k=i;
}
}
else{
l=pre[k];
if(a[l]==-1){
while(b[j]>=a[k]&&j) j--;
x[l]=b[j];use[j]=1;
}
k=l;
}
}j=1;
for(i=1;i<=n;i++){
if(a[i]==-1){
if(x[i]) a[i]=x[i];
else{
while(use[j]) j++;
a[i]=b[j];use[j]=1;
}
}
printf("%d ",a[i]);
}
return 0;
}
这里列几个自己犯的sb错误吧
- 最好不用upper_bound求第一个大于a[i]的数:这样就要判前面的数是否等于a[i],不等于才能更新,但等于时不更新就无法记录f[i],pre[i]的值了。
- 一开始我看了讲课PPT中的做法:每个x,找到一个最小的b(用来填空缺的数)满足b > G[x],然后用b来尝试更新G[x + 1]。这个过程可以用双指针优化,因此每次都是O(N + M)的。于是写出了这样一份代码。WA了无数遍,原因是这样虽然保证了对每个low[j]都是最小的大于它的b[k]来更新它,但不能保证对每个b[k]都是最大的小于它的low[j]被更新。也就是说执行low[j+1]=b[k]时,b[k]可能大于low[j+1],情况变劣了:
k=1;
for(j=0;j<=len;j++){
while(b[k]<=low[j]&&b[k]) k++;
if(b[k]){
low[j+1]=b[k];
pos[j+1]=i;
}
}
if(low[len+1]) len++;
- 有些做法是在序列后直接加一个inf以方便处理,我这里没有这么做,那么就要注意如果最后一个数是-1是不会被处理的,要特判。
- 这里不能写
while(len--)
,因为一开始就会减去1. - 别忘了写a[i]!=-1。