CF1144G Two Merged Sequences 题解
Two Merged Sequences
推销我的博客园。
题意
有一个长度为
若能,输出 YES 并在下一行给出方案,若不能,输出 NO。
输出方案的方法是对于每一位都输出
数据范围
思路
与 xhr 和 cn 巨佬一起完善出来了思路。
考虑使用动态规划。
- 状态:
dp_{i,0/1} 表示序列第i 位作为递增序列或者递减序列时的最优情况,0 代表在递增序列中,1 代表在递减序列中。
那么最优化属性呢?很显然,当你把当前位作为当前递增序列的最后一个时,递减序列的最后一个数越大越好,反之,若你把当前位作为当前递减序列的最后一个时,递增序列的最后一个数越小越好。
那么
那么转移就很显然了,这里拿
当上一个数小于当前数时,上一个数作为递增序列的最后一个的状态便可以转移至当前数作为递增序列的最后一个的状态。而如果上一个数作为递减序列时的最小递增序列末项的值小于当前数,便可以让当前数接到上一个数作为递减序列时的递增序列之后,那么此时的递减序列末项值为
总结如下:
其中
- 初始状态:
dp_{0,0}=10^9,dp_{0,1}=-10^9 。 - 合法目标状态:
dp_{n,0} \geqslant 0 和dp_{n,1} \leqslant 2 \times 10^5 。
那么怎么求出方案呢?很简单,用个数组记录当前状态是如何转移来的,最后逆推即可。
详细见代码。
复杂度
- 时间:
O(n) 。 - 空间:
O(n) 。
Code
#include <iostream>
using namespace std;
const int N = 2e5 + 10;
int n, a[N], dp[N][2], lst[N][2]; // lst 用于记录当前状态是如何转移来的,为 0 表示是由 dp[i - 1][0] 转移而来,否则由 dp[i - 1][1] 转移而来
void Output (int x, int y) { // 逆推求方案
if (!x) {
return ;
}
Output(x - 1, lst[x][y]);
cout << y << ' ';
}
int main () {
ios::sync_with_stdio(0), cin.tie(0);
cin >> n, dp[0][0] = 1e9, dp[0][1] = -1e9; // 初始状态
for (int i = 1; i <= n; i++) {
cin >> a[i], dp[i][0] = -1e9, dp[i][1] = 1e9;
// 下面是四种转移方式
if (a[i - 1] < a[i] || i == 1) {
dp[i][0] = dp[i - 1][0], lst[i][0] = 0;
}
if (dp[i - 1][1] < a[i] && a[i - 1] > dp[i][0]) {
dp[i][0] = a[i - 1], lst[i][0] = 1;
}
if (a[i - 1] > a[i] || i == 1) {
dp[i][1] = dp[i - 1][1], lst[i][1] = 1;
}
if (dp[i - 1][0] > a[i] && a[i - 1] < dp[i][1]) {
dp[i][1] = a[i - 1], lst[i][1] = 0;
}
// cout << dp[i][0] << ' ' << dp[i][1] << '\n'; // Debug
}
// 判断目标状态是否合法
if (dp[n][0] != -1e9) {
cout << "YES\n";
Output(n, 0);
} else if (dp[n][1] != 1e9) {
cout << "YES\n";
Output(n, 1);
} else {
cout << "NO";
}
return 0;
}