题目描述
小Z有一个01序列A=(A1,A2,A3,…,An)。他可以进行一次操作:选择一个区间L,R将其反转。
例如,对于序列A=(1,0,0,1,1),当L=2,R=4时,原序列将变为(1,1,0,0,1)。
小Z希望:通过这一次反转操作,使得这个序列的最长不下降子序列的长度尽可能的大,并想让你输出这个最大值。
一个序列的不下降子序列定义为:对于一个序列(p1,p2,…,pk)满足1≤p1<p2<…<pk≤n(n≤819200)且Ap1≤Ap2≤…≤Apk。则序列(Ap1,Ap2,…,Apk)为不下降子序列,其中k为这个不下降子序列的长度。
输入
一行一个01字符串,表示序列A
输出
一行一个正整数,表示最长不下降子序列长度的最大值。
样例输入
00111001101
样例输出
10
提示
一种最优策略为:反转区间[3,7],数列将变为(0,0,0,0,1,1,1,1,1,0,1) ,其最长不下降子序列的长度为10。
在一开始的时候以为反转某一段区间之后,只会影响反转的这一块中0或1的数量与前一段或后一段的0或1的数量
然后统计出来0或1的块的大小,只反转1…10…0这样的区间,其实是错的
答案会影响四段
wrong_code:
记录反转了这一块后前面0的个数与后面1的数量思路其实是错的
int n,k;
int a[maxn];
struct node{
int id;
int ct;
}b[maxn];
int main()
{
string s;
cin >> s;
n = s.size();
s = "#" + s;
int cnt = 0;
int t = 0;
int bak1 = 0;
for(int i=1;i<=n;i++){
++ t;
if(i == n || s[i] != s[i+1]){
b[++cnt].id = s[i] - '0';
b[cnt].ct = t;
t = 0;
if(s[i] == '1') bak1 += b[cnt].ct;
}
}
/*for(int i=1;i<=cnt;i++){
cout << b[i].id<<' '<<b[i].ct<<endl;
}*/
/// debug(bak1);
if(cnt == 1 || cnt == 2){
cout << n <<endl;
return 0;
}
int pre0 = 0;
int ans = 0;
b[0].id = 0,b[0].ct=0;
for(int i=1;i<cnt;i++){
if(b[i-1].id == 0) pre0 += b[i-1].ct;
else bak1 -= b[i-1].ct;
ans = max(ans,bak1 + pre0);
if(b[i].id == 1 && b[i+1].id == 0){
ans = max(ans,pre0 + b[i+1].ct + bak1);
/*debug(pre0);
debug(b[i+1].ct);
debug(bak1);*/
// cout << pre0 + b[i+1].ct + bak1 <<endl;
}
}
cout << ans <<endl;
return 0;
}
/**************************************************************
Problem: 13769
Language: C++
Result: 答案错误
****************************************************************/
ac_code:
int main()
{
string s;
cin >> s;
n = s.size();
s = "#" + s;
int t1,t2,t3,res;
t1 = t2 = t3 = res = 0;
for(int i=1;i<=n;i++){
if(s[i] == '0'){
++ t1;
t2 = max(t1,t2);
t3 = max(t2,t3 + 1);
res = max(res,t3);
}else{
t2 = max(t1,t2 + 1);
t3 = max(t2,t3);
res = max(t3,res + 1);
}
}
cout << res <<endl;
return 0;
}
/**************************************************************
Problem: 13769
Language: C++
Result: 正确
Time:40 ms
Memory:15588 kb
****************************************************************/
追更:
其实这道题和acwing上面,这道题是一样的
应该考虑答案影响的四段
最优质一定是在00000111110000011111
这种情况下的
我们用s1记录00000的最长长度
用s2 记录0000011111的最长长度
用s3 记录000001111100000的最长长度
用s4 记录00000111110000011111的最长长度
最终答案就是s3,s4中最大值
O(n)
该题code:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int main()
{
int n; cin >> n;
int s1 = 0,s2 = 0,s3 = 0,s4 = 0;
for(int i=1;i<=n;i++){
int x;scanf("%d",&x);
if(x == 1){
++ s1;
s3 = max(s2 + 1,s3 + 1);
}else{
s2 = max(s1 + 1,s2 + 1);
s4 = max(s3 + 1,s4 + 1);
}
}
cout << max(s3,s4) <<endl;
return 0;
}