zoukankan      html  css  js  c++  java
  • 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。
  • 相关阅读:
    老鼠走迷宫
    堆栈的应用 问题还未解决
    计数排序与基数排序
    C++类的描述
    选择排序 冒泡排序 插入排序
    计算名次
    多项式求值 n维多项式 Horner解法
    003_sublime如何实时预览markdown
    002_git下载的源码怎么打成jar包呢?
    001_idea常用插件
  • 原文地址:https://www.cnblogs.com/jstcao/p/15323065.html
Copyright © 2011-2022 走看看