P1410 子序列

Posted suxxsfe

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P1410 子序列相关的知识,希望对你有一定的参考价值。

dp
https://www.luogu.com.cn/problem/P1410

给定一个长度为 (N)(N) 为偶数)的序列,问能否将其划分为两个长度为 (N/2) 的严格递增子序列
多测,(Nle 2000)


不看题解果然还是没能想出来/kk

容易想到的:(f(i,j,p,q)),表示前 (i) 个数,有一个长度为 (j) 的最长上升子序列(还有一个长度为 (i-j) 的),它们的结尾分别是 (p,q),这种情况是否存在

发现 (p,q) 其中之一等于 (a_i),所以减少一维:(f(i,j,p)) 表示前 (i) 个数,有一个长度为 (j) 的最长上升子序列
(p) 表示两个子序列其中结尾不等于 (a_i) 的那个是多少。存的值当然还是是否存在
换种说法,就是前 (i) 个数分成两个子序列,一个长度为 (j),并以 (a_i) 结尾,另一个以 (p) 结尾的情况是否存在

进一步发现,对于给定的 (i,j),为了让后面更多的情况成立,总是希望 (p) 更小
所以没有必要把每个 (p) 的对应状态都存下来
那么:(f(i,j)),表示前 (i) 个数,有一个长度为 (j) 的最长上升子序列并以 (a_i) 结尾,另一个子序列(长度 (i-j))最小以什么数结尾
如果任意的 (p) 都不能使其成立,那么赋为无穷

所以考虑转移
如果 (a_i<a_{i+1}),那么可以直接把 (a_{i+1}) 选那个长度为 (j) 的子序列里去,就是 (f(i+1,j+1)=min(f(i+1,j+1),f(i,j)))
如果 (f(i,j)<a_{i+1}),可以把它选入长度为 (i-j) 的那个,(f(i+1,i-j+1)=min(f(i+1,i-j+1),a_{i}))
第二种转移改变(或者说成交换)了原来的两个子序列是否以当前的 (i) 结尾

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
#include<iomanip>
#include<cstring>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
	register int x=0;
	register char c=std::getchar();
	while(c<‘0‘||c>‘9‘){
		if((c=std::getchar())==EOF) return -1;
	}
	while(c>=‘0‘&&c<=‘9‘){x=x*10+(c^48);c=std::getchar();}
	return x;
}
int a[2005],f[2005][2005];
int n;
int main(){
	n=read();
	while(~n){
		for(reg int i=1;i<=n;i++) a[i]=read();
		a[n+1]=0;
		std::memset(f,0x3f,sizeof f);f[1][1]=-1;
		for(reg int i=1;i<=n;i++){
			for(reg int j=1;j<=i;j++)if(f[i][j]!=0x3f3f3f3f){
				if(a[i]<a[i+1]) f[i+1][j+1]=std::min(f[i+1][j+1],f[i][j]);
				if(f[i][j]<a[i+1]) f[i+1][i-j+1]=std::min(f[i+1][i-j+1],a[i]);
			}
		}
		std::puts(f[n][n/2]==0x3f3f3f3f?"No!":"Yes!");
		n=read();
	}
	return 0;
}










以上是关于P1410 子序列的主要内容,如果未能解决你的问题,请参考以下文章

P1410 子序列

P1410 子序列

P1410 子序列

Luogu P1410 子序列

洛谷 P1410 子序列(DP)

LCS(最长公共子序列)