给定一个长度为 nn 的数字序列 a1,a2,…,an,序列中只包含数字 1 和 2。
现在,你要选取一个区间 [l,r](1≤l≤r≤n),将 al,al+1,…,ar 进行翻转,并且使得到的新数字序列 a的最长非递减子序列的长度尽可能长。
请问,这个最大可能长度是多少?
一个非递减子序列是指一个索引为 p1,p2,…,pk 的序列,满足 p1<p2<…<pk 并且 ap1≤ap2≤…≤apk,其长度为 k。
输入格式
第一行一个整数 n。
第二行 n 个空格隔开的数字 1或 2,表示 a1,…,an
输出格式
输出一个整数,表示得到的新数字序列 aa 的最长非递减子序列的最大可能长度。
数据范围
对于 30的数据,1≤n≤100
对于 100的数据,1≤n≤106。
本题读入数据规模较大,需注意优化读入。
输入样例1:
4
1 2 1 2
输出样例1:
4
输入样例2:
10
1 1 2 2 2 1 1 2 2 1
输出样例2:
9
首先如果是求序列12的长度,那么
1111111111…1111111111…
– 只可能由 111111…111111… 这种状态转移而来
111111112222222222…111111112222222222…
– 可能由 111111…111111… 转移而来
– 也可能由 111112222…111112222… 转移而来
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
int s1 = 0, s2 = 0;
while (n--) {
int x;
cin >> x;
if (x == 1) {
s1++; // 如果当前数字是 1,则状态1的长度加1
} else {
s2 = max(s1 + 1, s2 + 1); // 如果当前数字为2,可能由两种状态转移而来
}
}
cout << max(s1, s2) << endl;
return 0;
}
回归本题,本题是加了反转
1.试试枚举
#include <bits/stdc++.h>
using namespace std;
int main() {
int s1 = 0, s2 = 0, s3 = 0, s4 = 0;
int n;
cin >> n;
while (n--) {
int x;
cin >> x;
if (x == 1) {
s1++;
s3 = max(s2 + 1, s3 + 1);
} else if (x == 2) {
s2 = max(s1 + 1, s2 + 1);
s4 = max(s3 + 1, s4 + 1);
}
}
cout << max(s3, s4) << endl;
return 0;
}
2.DP
记 f[i][j] 表示只考虑 a1∼ai 的子序列,且子序列的最后一个数在第 j部分的子序列集合。
属性为子序列长度的最大值。
情况 1:ai=1
因为 ai=1,所以 ai 不可能在第 2,4 部分,所以 f[i][2]=f[i−1][2] f[i][4]=f[i−1][4]。
而 aiai 可以在子序列中第 1,3部分,所以 f[i][1]=f[i−1][1]+1 f[i][3]=max(f[i−1][2],f[i−1][3])+1
情况 2:ai=2
与上面相反,由于人类的本质是复读机,所以很容易把上面的反过来:f[i][1]=f[i−1][1]
f[i][2]=max(f[i−1][1],f[i−1][2])+1
f[i][3]=f[i−1][3]
f[i][4]=max(f[i−1][3],f[i−1][4])+1
综上所述,有
f[i][1]=f[i−1][1]+[ai==1]
f[i][2]=max(f[i−1][1],f[i−1][2])+[ai==2]
f[i][3]=max(f[i−1][2],f[i−1][3])+[ai==1]
f[i][4]=max(f[i−1][3],f[i−1][4])+[ai==2]
复杂度 O(n)。
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1000005;
int n, x, res;
int f[N][4];
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i ++ )
{
scanf("%d", &x);
f[i][1] = f[i - 1][1] + (x == 1);
f[i][2] = max(f[i - 1][1], f[i - 1][2]) + (x == 2);
f[i][3] = max(f[i - 1][2], f[i - 1][3]) + (x == 1);
f[i][4] = max(f[i - 1][3], f[i - 1][4]) + (x == 2);
}
for (int i = 1; i <= 4; ++i) res = max(res, f[n][i]);
printf("%d\n", res);
return 0;
}
//滚动数组 空间O(1)
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1000005;
int n, x, res, f[5];
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i ++ )
{
scanf("%d", &x);
f[4] = max(f[3], f[4]) + (x == 2);
f[3] = max(f[2], f[3]) + (x == 1);
f[2] = max(f[1], f[2]) + (x == 2);
f[1] = f[1] + (x == 1);
}
for (int i = 1; i <= 4; ++i) res = max(res, f[i]);
printf("%d\n", res);
return 0;
}
欢迎评论点赞