ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

常见子序列DP模型

2022-05-17 21:35:02  阅读:83  来源: 互联网

标签:10 cnt 模型 mid long 序列 include DP


最长不下降(上升)子序列(LIS)

记 \(f_{len}\) 为长度为 \(len\) 的最长不下降子序列的最后一个数的最小值。

显然 \(f\) 单调不降。

记 \(cnt\) 为当前 \(f\) 更新到的最大长度。

正向枚举原数列 \(a\),如果 \(a_i \ge f_{cnt}\),表明可以继续把最长的子序列再增长一个长度,即 \(f_{++cnt}=a_i\)。而对于长度比 \(cnt\) 更短的 \(f\) 值,\(a_i\) 显然更新不了它们。因为 \(a_i\) 显然比他们大,显然我们希望最后一个数尽可能小,从而可以往后加更多的数。

如果 \(a_i<f_{cnt}\),则二分找出一个 \(f_j\) 使得其长度尽可能大并小于等于 \(a_i\),然后把 \(f_j\) 改成 \(a_i\)。显然 \(a_i\) 只能更新这一个 \(f\)。

最后答案就是 \(cnt\)。

时间复杂度 \(O(n \log n)\)。

//LIS
memset(f,0x3f,sizeof(f));
f[1]=a[1];
cnt=1;
for(i=2;i<=n;i++){
	l=1;r=cnt+1;
	while(l<r){
		mid=(l+r)/2;
		if(f[mid]>a[i])
			r=mid;
		else
			l=mid+1;
	}
	f[l]=a[i];
	if(l==cnt+1) cnt++;
}
cout<<cnt<<endl;

最长公共子序列(LCS)

记 \(f_{i,j}\) 表示 \(a_1,a_2,...,a_{i}\) 与 \(b_1,b_2,...,b_j\) 的最长公共子序列长度。

有如下转移方程:

\[f_{i,j}= \begin{cases} f_{i-1,j-1},a_i=b_j\\ \max({f_{i-1,j},f_{i,j-1}}),a_i \ne b_j\\ \end{cases} \]

时间复杂度 \(O(n^2)\)。

n=s.size(); m=t.size();
for(i=1;i<=n;i++){
	for(j=1;j<=m;j++){
		if(s[i-1]==t[j-1]){
			f[i][j]=f[i-1][j-1]+1;
		}
		else{
			f[i][j]=max(f[i][j-1],f[i-1][j]);
		}
	}
}
cout<<f[n][m]<<endl;

如果给的 \(a\) 和 \(b\) 是排列(P1439),那么有 \(O(n \log n)\) 的做法:挼行止爷爷的题解

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const long long N=1e5+1;
long long n,a[N+10],tr[N+10],f[N+10],cnt;

int main(){
	long long i,j,u,v;
	scanf("%lld",&n);
	for(i=1;i<=n;i++)
		scanf("%lld",&a[i]);
	for(i=1;i<=n;i++){
		scanf("%lld",&u);
		tr[u]=i;
	}
	for(i=1;i<=n;i++) a[i]=tr[a[i]];
	f[1]=a[1];cnt=1;
	long long l,r,mid;
	for(i=2;i<=n;i++){
		l=1,r=cnt+1;
		while(l<r){
			mid=l+r>>1;
			if(f[mid]>a[i]) r=mid;
			else l=mid+1;
		}
		f[l]=a[i];
		if(l>cnt) cnt++;
	}
	printf("%lld\n",cnt);
	return 0;
}

最长公共上升子序列(LCIS)

设 \(f_{i,j}\) 表示在 \(a\) 的前 \(i\) 个数,\(b\) 的前 \(j\) 个数,且以 \(b_j\) 结尾的 LCIS 长度。

有转移 \(\begin{cases} f_{i,j}=f_{i-1,j}\\f_{i,j}=\max{f_{i-1,k}}+1,a_i=b_j \end{cases}\)

发现后面那个转移可以记一下。于是就 \(O(n^2)\) 做完了。

输出方案的话记录一下前面更新的是哪一个就好了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

inline long long read(){char ch=getchar();long long x=0,f=1;while(ch<'0' || ch>'9'){if(ch=='-') f=-1;ch=getchar();}
                        while('0'<=ch && ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
inline void write(long long x){if(x<0){putchar('-');x=-x;}if(x>9) write(x/10);putchar(x%10+'0');}
const long long N=501;
long long n,a[N+10],m,b[N+10],f[N+10][N+10],ans,mx,id,pre[N+10][N+10],s[N+10],tot;

int main(){
	long long i,j,u,v;
	n=read();for(i=1;i<=n;i++) a[i]=read();
	m=read();for(i=1;i<=m;i++) b[i]=read();
	for(i=1;i<=n;i++){
		mx=0,id=0;
		for(j=1;j<=m;j++){
			pre[i][j]=pre[i-1][j];
			f[i][j]=f[i-1][j];
			if(a[i]==b[j]){
				if(f[i][j]<mx+1){
					f[i][j]=mx+1;
					pre[i][j]=id;
				}
			}
			else if(a[i]>b[j]){
				if(f[i-1][j]>mx){
					mx=f[i-1][j];
					id=j;
				}
			}
		}
	}
	id=0;
	for(i=1;i<=m;i++){
		if(ans<f[n][i]){
			ans=f[n][i];
			id=i;
		}
	}
	write(ans),putchar('\n');
	for(;id;id=pre[n][id]){
		s[++tot]=b[id];
	}
	while(tot){
		write(s[tot--]),putchar(' ');
	}
	putchar('\n');
	return 0;
} 

标签:10,cnt,模型,mid,long,序列,include,DP
来源: https://www.cnblogs.com/Gokix/p/chang-jian-zi-xu-lie-dp-mo-xing.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有