题目大意
给定两个长为$n$的$01$串$A,B$,每次操作有三种
将$A$整体向左移动,并将$A_1$放在原来$A_n$的位置上。
将$A$整体向有移动,并将$A_n$放在原来$A_1$的位置上。
选择一个位置$i$使得$B_i=1$,将$A_i$变为$1-A_i$。
求将$A$变为$B$的最小操作数,若无解输出$-1$。
$nleq 2000$
题解
首先当$A$有$1$,$B$没有$1$那么一定无解。当$A,B$均没有$1$时代价为$0$
其余情况$B$中一定有$1$,将$A$的环状移动用将$B$左右倍长为$3$倍,$A$直接左右移动。
对于$A$中每一个位置$i$,预处理每一个$i$需要向左和向右最少移动多少步才能使得$A_i$对应了一个$B_j$使得$B_j=1$,记为$L_i,R_i$。
接着,考虑暴力枚举最终$A$在与原来的相对位置与$B$对应,假设$A$与原来的位置相对向右移动了$k$个,然后对于每一个$A_i e B_{i+k}$,我们至少将$A$左移$L_i$位或右移$R_i$。
假设我们最终要向右移动$k$位与$B$对应,考虑枚举先向左走了$x$步,那么对于所有$L_ileq x$对答案无影响了,只需要统计对$L_i>x$的$R_i$最大值(记为$RS$),那么但就是先向左移$x$步再移回来,再向右移到$RS$,最后移到$k$显然是最优的方案,过程中要对哦每一个$A_i e B_{i+k}$的进行一步操作,记为$m$个,那么就用$2x+RS+|RS-k|+m$来更新答案。
将$A$向左移动同理。
复杂度$O(n^2)$
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<cmath> #define LL long long #define M 8010 using namespace std; int n,m,p[M],t[M],L[M],R[M]; char s1[M],s2[M]; int pre[M],suf[M],ls[M],rs[M],ct1,ct2,ans; bool cmp(int x,int y){return L[x]>L[y]||(L[x]==L[y]&&R[x]<R[y]);} void upd(int tar,int hd,int rev,int con){ if(tar>=hd) ans=min(ans,rev*2+tar+con); else ans=min(ans,rev*2+(hd*2-tar)+con); } int main(){ scanf("%s%s",s1+1,s2+1),n=strlen(s1+1),ans=n*n; for(int i=1;i<=n;i++) p[i]=s1[i]-'0',t[i]=t[i+n]=t[i+n+n]=s2[i]-'0'; for(int i=1;i<=n;i++) ct1+=p[i],ct2+=t[i]; if(!ct2){puts(ct1?"-1":"0");return 0;} for(int i=1;i<=n+n;i++) if(!t[i]) ls[i]=(ls[i-1]+1); for(int i=n+n+n;i>n;i--) if(!t[i]) rs[i]=(rs[i+1]+1); for(int k=0;k<=n;k++){ m=0,memset(pre,0,sizeof(pre)),memset(suf,0,sizeof(suf)); for(int i=1;i<=n;i++){ if(p[i]^t[i+k]){ m++; if(t[i+k]) continue; pre[ls[i+n]]=max(pre[ls[i+n]],rs[i+n]); suf[rs[i+n]]=max(suf[rs[i+n]],ls[i+n]); } } for(int i=n-1;i>=0;i--) suf[i]=max(suf[i],suf[i+1]),pre[i]=max(pre[i],pre[i+1]); for(int i=0;i<n;i++) upd(k,pre[i+1],i,m),upd(n-k,suf[i+1],i,m); } printf("%d ",ans); return 0; }