专栏文章

题解:P1410 子序列

P1410题解参与者 4已保存评论 3

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
3 条
当前快照
1 份
快照标识符
@mimzr39z
此快照首次捕获于
2025/12/01 18:13
3 个月前
此快照最后确认于
2025/12/01 18:13
3 个月前
查看原文
很神的一道 DP!!
捋一下我们需要的状态:
  • 第一个子序列的当前末尾元素。
  • 第二个子序列的当前末尾元素。
  • 第一个子序列的长度。
  • 第二个子序列的长度。
但是 n2000n\le2000,需要考虑优化。
假设枚举到了第 aia_i 时,前 i1i-1 已经被分配完,那么这两个子序列的长度之和等于 i1i-1。这样就只需要记录其中一个子序列的长度即可。
发现如果枚举到了第 aia_i,那么 ai1a_{i-1} 一定在其中一个子序列的末尾。这样就只需要记录另一个子序列的末尾元素即可,如果另一个子序列的长度不变,我们要希望这个子序列的末尾尽可能小。
设状态 f(i,j)f(i,j) 表示分配完了前 ii 个元素,其中末尾为 aia_i 的子序列的长度为 jj 时,另一个子序列的末尾为 f(i,j)f(i,j)。显然另一个子序列的长度为 iji-j,这样就能用二维表示四个状态。
如果不能分配,f(i,j)f(i,j) 就表示正无穷。
转移就是:
  • 如果 ai>ai1a_i>a_{i-1},那么 f(i,j)f(i1,j1)f(i,j) \leftarrow f(i-1,j-1)。相当于插入末尾是 ai1a_{i-1} 的子序列中。
  • 如果 ai>f(i1,j)a_i>f(i-1,j),那么 f(i,ij)ai1f(i,i-j) \leftarrow a_{i-1}。相当于插入末尾不是 ai1a_{i-1} 的子序列中。
时间复杂度 O(n2)O(n^2)
CPP
#include <bits/stdc++.h>
using namespace std;
const int N=2005;
int f[N][N];
int n,a[N];
void sol(){
	for(int i=1;i<=n;++i) scanf("%d",&a[i]);
	memset(f,0x7f,sizeof f);
	f[0][0]=a[0]=-1;
	for(int i=1;i<=n;++i){
		if(a[i-1]<a[i]) for(int j=1;j<=i;++j)
			f[i][j]=f[i-1][j-1];
		for(int j=1;j<i;++j) if(f[i-1][j]<a[i])
			f[i][i-j]=min(f[i][i-j],a[i-1]);
	}
	puts(f[n][n/2]>1e9?"No!":"Yes!");
}
int main(){
	while(scanf("%d",&n)==1) sol();
}

评论

3 条评论,欢迎与作者交流。

正在加载评论...