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
",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
",ans);
	//printf("%d %d
",ed,len[ed]);
	for(ri i=1;i<=len[ed];i++)printf("%d ",path[ed][i]);
	return 0;
}

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

原文地址:https://www.cnblogs.com/Rye-Catcher/p/9579397.html