一道感觉挺神的题目。
考虑在操作中寻找一些方便计算的东西用来转化。
假设在对第 (i(i eq 1,i eq n)) 个位置操作,那么该操作合法的一个必要条件是 (s_{i-1} eq s_{i+1}) ,那么该串中同色的连续段个数不会变。
考虑对于每一个 (s_i
eq s_{i+1}) 的位置做一个标记,且若 (s_i=0) ,标记为 (0) ,若 (s_i=1) ,标记为 (1) 。
可以发现:
- 把标记连在一起之后,必定是 (0,1) 交替的样子
- 一次操作就是将一个标记左移或右移一位
- 最后的目标就是将所有标记对齐
- 一个状态合法,当且仅当任意两个标记距离小于等于 (2) ,且中间位置的标记互不重合
但这有一个问题,没办法处理两端的位置。
考虑在两端继续补充无穷多的标记,且这些标记满足 (0,1) 交替,那么可以通过中间的标记和两端的标记互相转移,处理两端的位置的修改操作。
最后考虑对于两个标记的序列,如何计算答案。
- 一个结论是直接计算对应标记的距离和。
虽然这个结论没有考虑序列的合法性,但是这个结论是正确的。考虑改变移动标记的顺序,可以感性地理解。
但是由于两端标记的存在,必须用 (O(n)) 的时间枚举两端标记的个数,然后一次答案的计算是 (O(n)) 的。
所以得到了总复杂度为 (O(n^2)) 的做法。
题外话:Atcoder 里思维题还是挺好的(思维难度大,代码难度小)。
Code:(感觉不是很好看
#define N 20005
char s[N],t[N];
int a[N],b[N],ta[N],tb[N];
int c1,c2,n;
signed main()
{
cin>>n;
scanf("%s%s",s+1,t+1);
if(s[1]=='1') ta[++c1]=0;
for(int i=1;i<n;i++) if(s[i]!=s[i+1]) ta[++c1]=i;
if(s[n]=='1') ta[++c1]=n;
if(t[1]=='1') tb[++c2]=0;
for(int i=1;i<n;i++) if(t[i]!=t[i+1]) tb[++c2]=i;
if(t[n]=='1') tb[++c2]=n;
for(int i=1;i<=c1;i++) a[i+c2]=ta[i];
for(int i=c1+c2+1;i<=c1+c2+c2;i++) a[i]=n;
int ans=1e9;
for(int i=0;i<=c2+c1;i+=2)
{
for(int j=1;j<=i;j++) b[j]=0;
for(int j=1;j<=c2;j++) b[i+j]=tb[j];
for(int j=i+c2+1;j<=c1+c2+c2;j++) b[j]=n;
int R=0;
for(int j=1;j<=c1+c2+c2;j++) R+=abs(a[j]-b[j]);
if(R<ans) ans=R;
}
cout<<ans<<endl;
return 0;
}