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错误吧

  1. 最好不用upper_bound求第一个大于a[i]的数:这样就要判前面的数是否等于a[i],不等于才能更新,但等于时不更新就无法记录f[i],pre[i]的值了。
  2. 一开始我看了讲课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++;
  1. 有些做法是在序列后直接加一个inf以方便处理,我这里没有这么做,那么就要注意如果最后一个数是-1是不会被处理的,要特判。
  2. 这里不能写while(len--),因为一开始就会减去1.
  3. 别忘了写a[i]!=-1。
原文地址:https://www.cnblogs.com/jstcao/p/15323065.html