agc019D Shift and Flip

题目链接

https://agc019.contest.atcoder.jp/tasks/agc019_d

题意简述

有两个0/1串 a , b a,b a,b,可以对 a a a串执行以下操作:

  • 向左旋转 a a a串, a 1 , a 2 , ⋯   , a n a_1,a_2,\cdots ,a_n a1,a2,,an变成 a 2 , a 3 , ⋯   , a n , a 1 a_2,a_3,\cdots ,a_n,a_1 a2,a3,,an,a1
  • 向右旋转 a a a串, a 1 , a 2 , ⋯   , a n a_1,a_2,\cdots ,a_n a1,a2,,an变成 a n , a 1 , a 2 , ⋯   , a n − 1 a_n,a_1,a_2,\cdots ,a_{n-1} an,a1,a2,,an1
  • b i = 1 b_i=1 bi=1时反转 i i i位置,0变1,1变0。

求最少多少次操作后 a , b a,b a,b串相同。

题解

枚举 a a a串向右旋转了多少次,向左旋转同理。显然可以得出旋转后 a i ̸ = b i a_i\not =b_i ai̸=bi的位置。假设旋转过程中这些位置都没有对上一个 b i = 1 b_i=1 bi=1的位置,那么要么开始向左旋转一个位置然后转回来,要么转到目标位置后向右旋转一个位置然后转回去。对于每个上述位置,如果开始向左旋转的长度 ≤ k \leq k k时就必须向右旋转,因此可以处理出来然后前缀和一遍求出向左旋转对应需要向右旋转的长度。

代码

#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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值