题面
h
u
a
j
i
huaji
huaji有一个 01
序列,每次可以对其中的某一位取反(
0
0
0变
1
1
1,
1
1
1变
0
0
0)
求最少翻转其中的几位可以使得该序列变为非递减序列
输入格式
第一行输入一个整数 n n n ( 1 ≤ n ≤ 1 0 6 1≤n≤10^6 1≤n≤106)
第二行输入一个长度为
n
n
n 的且仅包含 0
和 1
的字符串
输出格式
输出一个整数,为该序列变为非递减序列的最少操作次数
输入样例
6
010110
输出样例
2
解题思路
其实题意描述有一点问题,求的应该是将该序列变为单调不减的01序列(因为没有单调性其实也是非递减)。
我们的目标具体来说就是选定某个位置,之前的均为0,之后的均为1,同时保证与原序列相似性最高。
根据这个目标,我们只需要遍历每一个可能的位置,选出其中需要操作次数最少的就可以了。
问题在于如何快速获取操作次数。
因为只有两种元素:0和1。所以我们只需要知道其中一种的数量,剩下的都是另外一种。
那么采用前缀和的概念,累计指定位置前 0 0 0的数量。
//首元素特殊处理
if (str[0] == '0') zero_counter[0] = 1;
else one_counter[0] = 1;
for (int i = 1; i < len; i++) {
zero_counter[i] = zero_counter[i - 1];
if (str[i] == '0') zero_counter[i]++;
}
然后遍历每一个位置即可:
int ans = zero_counter[len - 1];//目标序列没有0
for (int i = 0; i < len; i++) {
ans = min(ans, (i + 1 - zero_counter[i]) + (zero_counter[len - 1] - zero_counter[i]));
}
最后,AC代码如下:
#include <iostream>
using namespace std;
const int max_len = 1e6;
int zero_counter[max_len];
int main() {
int n;
cin >> n;
string str;
cin >> str;
int len = str.size();
if (str[0] == '0') zero_counter[0] = 1;
for (int i = 1; i < len; i++) {
zero_counter[i] = zero_counter[i - 1];
if (str[i] == '0') zero_counter[i]++;
}
int ans = zero_counter[len - 1];//目标序列没有0
for (int i = 0; i < len; i++) {
ans = min(ans, (i + 1 - zero_counter[i]) + (zero_counter[len - 1] - zero_counter[i]));
}
cout << ans << endl;
return 0;
}