题意: 给定两个
序列
和
,可以选择
中的子序列
进行一次顺时针操作,即:
然后
。问是否可以通过不断地操作使得
,如果可以输出最小操作次数,否则输出
。
题解: 无解的情况就是
或者
。
之后,对于
自然无需调整,所以我们将
的部分组成一个新的序列
对于
存储的是
的
。
需要了解的是:
-
可以知道, 中 和 的数量一定一样多,如果不一样多则无法得到 。设 ,那么在对应的T中 ,所以 ,即两者数量一定相等。
-
可以知道,每次操作一定选择的是 或
因为 中对应的 对应 中的1,所以我们每次需要把 中的 和 调换位置,进行一次顺时针移动后就可以得到当前选择的序列中各个元素相较于移动前都发生了改变,即 。
如果选择的非此种序列,那么至少存在连续两个元素相同,那么顺时针移动一次必然不能使得移动前后所有元素都发生改变。如 移动后成 ,第三个元素未变。选择成为 或 就一定能保证只需移动一次就可以翻转所有元素。而其他情况移动成功情况至少要移动次数为max(max(连续的1个数),max(连续的0个数))。 -
开始选取元素:我们尽可能保证当前待选的元素放到序列后成为使得序列成为 或者 这两种,如果这两种不行,则就会出现某一个序列被加上当前待选元素后成为 或者 。由于尽可能少操作,即序列尽可能少,假设当前待选元素为 ,那么优先考虑加到 这种序列后面,如果这种序列不存在,那么考虑加到 这种序列后面,如果这种序列也不存在,那么考虑自己开辟一个新的序列,以 开头,此时操作数加 。待选元素为 同理。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 1000010;
char s[N];
char t[N];
int str[N];
int g;
int n;
int main()
{
scanf("%d", &n);
scanf("%s", s + 1);
scanf("%s", t + 1);
int cnts[2] = {0}, cntt[2] = {0};
for(int i = 1; i <= n; i++) {
cnts[s[i] - '0']++;
cntt[t[i] - '0']++;;
if(s[i] == t[i]) continue;
str[++g] = i;
}
if(cnts[0] != cntt[0] || cnts[1] != cntt[1]) puts("-1");
else {
//cnt[i][j]表示第一个是i的当前为j的
int cnt[2][2] = {0, 0, 0, 0}, res = 0;
for(int i = 1; i <= g; i++) {
if(s[str[i]] == '0') {
if(cnt[1][1]) cnt[1][1]--, cnt[1][0]++;
else if(cnt[0][1]) cnt[0][1]--, cnt[0][0]++;
else cnt[0][0]++, res++;
}
else {
if(cnt[0][0]) cnt[0][0]--, cnt[0][1]++;
else if(cnt[1][0]) cnt[1][0]--, cnt[1][1]++;
else cnt[1][1]++, res++;
}
}
printf("%d\n", res);
}
return 0;
}