agc019D Shift and Flip

题目链接

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

题意简述

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

  • 向左旋转aaa串,a1,a2,⋯ ,ana_1,a_2,\cdots ,a_na1,a2,,an变成a2,a3,⋯ ,an,a1a_2,a_3,\cdots ,a_n,a_1a2,a3,,an,a1
  • 向右旋转aaa串,a1,a2,⋯ ,ana_1,a_2,\cdots ,a_na1,a2,,an变成an,a1,a2,⋯ ,an−1a_n,a_1,a_2,\cdots ,a_{n-1}an,a1,a2,,an1
  • bi=1b_i=1bi=1时反转iii位置,0变1,1变0。

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

题解

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

代码

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

转载于:https://www.cnblogs.com/Canopus-wym/p/10376094.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值