专栏文章
题解:P1410 子序列
P1410题解参与者 4已保存评论 3
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 3 条
- 当前快照
- 1 份
- 快照标识符
- @mimzr39z
- 此快照首次捕获于
- 2025/12/01 18:13 3 个月前
- 此快照最后确认于
- 2025/12/01 18:13 3 个月前
很神的一道 DP!!
捋一下我们需要的状态:
- 第一个子序列的当前末尾元素。
- 第二个子序列的当前末尾元素。
- 第一个子序列的长度。
- 第二个子序列的长度。
但是 ,需要考虑优化。
假设枚举到了第 时,前 已经被分配完,那么这两个子序列的长度之和等于 。这样就只需要记录其中一个子序列的长度即可。
发现如果枚举到了第 ,那么 一定在其中一个子序列的末尾。这样就只需要记录另一个子序列的末尾元素即可,如果另一个子序列的长度不变,我们要希望这个子序列的末尾尽可能小。
设状态 表示分配完了前 个元素,其中末尾为 的子序列的长度为 时,另一个子序列的末尾为 。显然另一个子序列的长度为 ,这样就能用二维表示四个状态。
如果不能分配, 就表示正无穷。
转移就是:
- 如果 ,那么 。相当于插入末尾是 的子序列中。
- 如果 ,那么 。相当于插入末尾不是 的子序列中。
时间复杂度 。
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 条评论,欢迎与作者交流。
正在加载评论...