CF10D-LCIS题解--线性DP+打印方案

题目链接:

https://www.luogu.org/problemnew/show/CF10D

方法一

分析

\(LCS\)\(LIS\)已经成烂大街的知识了,可是当这两个合并起来成为\(LCIS\),解决的方式方法也多了起来.

首先有种最朴素的\(O(N^4)\)方法,\(f[i][j]\)表示A串第\(i\)个字母和B串第\(j\)个字母结尾的状态中\(LCIS\)的长度,那么

那么如果\(a[i]==b[j],f[i][j]=max_{0<=k<j且b[k]<a[i](b[j])}(f[i-1][k])+1\)

否则\(f[i][j]=f[i-1][j]\)

但是这种方法怎么打印方案呢?我们用\(path[j][len[j]]\)表示以\(j\)结尾的\(LCIS\)方案,\(len[j]\)指的是以\(j\)结尾的\(LCIS\)长度

这样我们从\(k\)更新到\(j\)时,首先将\(path[k][len[k]]\)全部复制到\(path[j][len[j]]\);

然后\(len[j]=len[k]+1,path[j][len[j]]=b[j]\)

跑时950+ms

代码

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cctype>
#include <queue>
#include <vector>
#define ll long long 
#define ri register int 
using std::max;
using std::min;
using std::swap;
template <class T>inline void read(T &x){
	x=0;int ne=0;char c;
	while(!isdigit(c=getchar()))ne=c=='-';
	x=c-48;
	while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+c-48;
	x=ne?-x:x;return ;
}
const int maxn=505;
const int inf=0x7fffffff;
int n,m,a[maxn],b[maxn],f[maxn][maxn],len[maxn];
int path[maxn][maxn];
void print(int x){
 	for(ri i=1;i<=len[x];i++)printf("%d ",path[x][i]);
 	puts("");
	return ;
}
int main(){
	int x,y,z;
	int ans=-inf,ed=0;
	read(n);
	for(ri i=1;i<=n;i++){read(a[i]);}
	read(m);
	for(ri i=1;i<=m;i++){read(b[i]);}
	a[0]=b[0]=-inf;
	for(ri i=1;i<=n;i++){
		for(ri j=1;j<=m;j++){
			if(a[i]==b[j]){
				for(ri k=0;k<j;k++){
					if(b[k]<a[i]){
						//f[i][j]=max(f[i][j],f[i-1][k]+1);
						if(f[i][j]<f[i-1][k]+1){
							f[i][j]=f[i-1][k]+1;
							len[j]=len[k]+1;
							for(ri p=1;p<=len[k];p++)path[j][p]=path[k][p];
						}
					}
				}
			}
			else f[i][j]=f[i-1][j];
			//ans=max(ans,f[i][j]);
			path[j][len[j]]=b[j];
			if(ans<f[i][j]){
				ans=f[i][j];
				ed=j;
			}
		}
	}
	printf("%d\n",ans);
	print(ed);	
	return 0;
}

方法二

我们考虑递推时的决策集合,\(f[i][j]\)都是由\(f[i][k](b[k]<a[i])\)递推得到,那么我们如果在从\(f[i][0]\)递推到\(f[i][j]\)时我们已经记录下所有\(f[i][k]\)的最大值设为\(val\),直接将\(f[i][j]\)设为\(max(f[i][j],val+1)\)就好了,打印路径的方法跟方法一类似

这样时间复杂度能少个\(N\)

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <queue>
#include <algorithm>
#define ll long long 
#define ri register int 
#define ull unsigned long long 
using std::min;
using std::max;
using std::swap;
template <class T>inline void read(T &x){
	x=0;int ne=0;char c;
	while(!isdigit(c=getchar()))ne=c=='-';
	x=c-48;
	while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+c-48;
	x=ne?-x:x;return ;
}
const int maxn=505;
const int inf=0x7ffffff;
int n,m,a[maxn],b[maxn],f[maxn][maxn],path[maxn][maxn],len[maxn],ed;
int main(){
	read(n);
	for(ri i=1;i<=n;i++){
		read(a[i]);
	}
	read(m);
	for(ri i=1;i<=m;i++){
		read(b[i]);
	}
	int ans=-inf,val,lst=0;
	for(ri i=1;i<=n;i++){
		lst=0;
		val=f[i-1][0];
		for(ri j=1;j<=m;j++){
		   if(a[i]==b[j]){
		   	  if(val+1>f[i][j]){
		   	  	 f[i][j]=val+1;
		   	  	 for(ri k=1;k<=len[lst];k++)path[j][k]=path[lst][k];
		   	  	 len[j]=len[lst]+1;	   	  	 
			  }
		   }
		   else f[i][j]=f[i-1][j];
		   path[j][len[j]]=b[j];
		   //ans=max(ans,f[i][j]);
		   if(f[i][j]>ans){
		   	ans=f[i][j];
		   	ed=j;
		   }
		   if(b[j]<a[i]){
		   		//val=max(val,f[i-1][j]);
		   		if(val<f[i-1][j]){
		   			val=f[i-1][j];
		   			lst=j;
				}
		   }
		}
	}	
	printf("%d\n",ans);
	//printf("%d %d\n",ed,len[ed]);
	for(ri i=1;i<=len[ed];i++)printf("%d ",path[ed][i]);
	return 0;
}

当然题解中还有\(O(1)\)记录路径的方法,以及\(O(N)\)的空间复杂度方法,这里先挖个坑吧

posted @ 2018-09-03 16:53  Rye_Catcher  阅读(197)  评论(0编辑  收藏  举报