Agc019_D Shift and Flip

传送门

题目大意

给定两个长为$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;
}
原文地址:https://www.cnblogs.com/OYJason/p/9830552.html