版权声明:本文为博主原创文章,未经博主允许可以转载,但要注明出处 https://blog.csdn.net/wang3312362136/article/details/85014823
题目链接
https://agc019.contest.atcoder.jp/tasks/agc019_d
题意简述
有两个0/1串 ,可以对 串执行以下操作:
- 向左旋转 串, 变成 。
- 向右旋转 串, 变成 。
- 当 时反转 位置,0变1,1变0。
求最少多少次操作后 串相同。
题解
枚举 串向右旋转了多少次,向左旋转同理。显然可以得出旋转后 的位置。假设旋转过程中这些位置都没有对上一个 的位置,那么要么开始向左旋转一个位置然后转回来,要么转到目标位置后向右旋转一个位置然后转回去。对于每个上述位置,如果开始向左旋转的长度 时就必须向右旋转,因此可以处理出来然后前缀和一遍求出向左旋转对应需要向右旋转的长度。
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
int read()
{
int x=0,f=1;
char ch=getchar();
while((ch<'0')||(ch>'9'))
{
if(ch=='-')
{
f=-f;
}
ch=getchar();
}
while((ch>='0')&&(ch<='9'))
{
x=x*10+ch-'0';
ch=getchar();
}
return 0;
}
const int maxn=2000;
const int inf=0x3f3f3f3f;
int n,ans,a[maxn+10],b[maxn+10],sum[maxn+10],nxt[maxn+10],past[maxn+10],f[maxn+10];
char tmp[maxn+10];
inline int trans(int i,int x)
{
if(i+x>n)
{
return i+x-n;
}
return i+x;
}
inline int getsum(int l,int r)
{
if(r>=l)
{
return sum[r]-sum[l-1];
}
return sum[n]-sum[l-1]+sum[r];
}
int solve()
{
for(int i=1; i<=n; ++i)
{
sum[i]=sum[i-1]+b[i];
}
for(int i=1; i<=n; ++i)
{
for(int j=0; j<n; ++j)
{
if(getsum(i,trans(i,j)))
{
nxt[i]=j;
break;
}
}
for(int j=0; j<n; ++j)
{
if(getsum(trans(i,n-j),i))
{
past[i]=j;
break;
}
}
}
for(int i=0; i<n; ++i)
{
memset(f,0,sizeof f);
int cnt=0;
for(int j=1; j<=n; ++j)
{
if(a[j]!=b[trans(j,i)])
{
++cnt;
if(nxt[j]-i>0)
{
f[past[j]-1]=std::max(f[past[j]-1],nxt[j]-i);
}
}
}
for(int j=n-1; j>=0; --j)
{
f[j]=std::max(f[j],f[j+1]);
ans=std::min(ans,(f[j]+j)*2+i+cnt);
}
}
return 0;
}
int main()
{
scanf("%s",tmp+1);
n=strlen(tmp+1);
for(int i=1; i<=n; ++i)
{
a[i]=tmp[i]-'0';
}
scanf("%s",tmp+1);
for(int i=1; i<=n; ++i)
{
b[i]=tmp[i]-'0';
}
int flag=0;
for(int i=1; i<=n; ++i)
{
if(b[i])
{
flag=1;
break;
}
}
if(!flag)
{
for(int i=1; i<=n; ++i)
{
if(a[i])
{
flag=1;
break;
}
}
puts(flag?"-1":"0");
return 0;
}
ans=inf;
solve();
std::reverse(a+1,a+n+1);
std::reverse(b+1,b+n+1);
solve();
printf("%d\n",ans);
return 0;
}