1. 程式人生 > >[agc019d]Shift and Flip

[agc019d]Shift and Flip

前言

這是一個簡單題但是我細節一開始沒想清楚?

題目大意

兩個01字串a和b,你可以把a左旋、右旋。
還有一種翻轉操作,如果bi=1那麼你可以把ai取反。
問a變成b最少操作次數。

做法

先判斷無解,當b中有1時一定有解,全0時如果a不是全0就會GG。
然後看看怎麼算答案。
先對每個位置預處理至少左移/右移多少次才能讓它對應一個b中有1的位置,分別記為L和R。
我們可以列舉最後的對應位置i。
然後我們可以考慮最終是右移對齊b的還是左移對齊b的。
以右移為例。
假如要右移cnt步。
我們考慮所有R>cnt的需要翻轉的位置,你要麼只能再右移長一點,最後再左移回來,要麼你要嘗試一開始左移至少L步。
也就是對於每一個這樣的位置,都有兩種選擇,左移滿足或右移滿足。
如果全部選擇左移滿足,那麼在移動的步數上是最大左移步數*2+cnt。
如果選擇過右移滿足,那麼在移動的步數上是最大左移步數*2+最大右移步數*2-cnt。
我們可以列舉最大左移步數,然後找到對應的最大右移步數。
計算最小答案即可。
只需要最小化移動步數就行了,翻轉操作的次數是固定的。
複雜度我寫的是n^2 log n,-_-。

#include<cstdio>
#include<algorithm>
#include<cstring>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
const int maxn=2000+10;
char s[maxn];
int a[maxn],b[maxn],c[maxn],L[maxn],R[maxn];
bool bz[maxn];
int i,j,k,l,t,n,m,ans,num,sum,cnt,mx,mi,top;
bool czy,gjx;
bool cmpL(int
x,int y){ return L[x]<L[y]; } bool cmpR(int x,int y){ return R[x]<R[y]; } int main(){ scanf("%s",s+1); n=strlen(s+1); czy=1; fo(i,1,n){ a[i]=s[i]-'0'; if (a[i]) czy=0; } scanf("%s",s+1); gjx=1; fo(i,1,n){ b[i]=s[i]-'0'; if (b[i]) gjx=0
; } if (gjx){ if (czy) printf("0\n");else printf("-1\n"); return 0; } fo(i,1,n){ j=i; while (!b[j]){ L[i]++; if (j==1) j=n; else j--; } j=i; while (!b[j]){ R[i]++; if (j==n) j=1; else j++; } } ans=1000000000; fo(i,1,n){ fo(j,1,n) bz[j]=0; sum=0; j=i; fo(k,1,n){ if (a[j]!=b[k]) bz[j]=1,sum++; if (j==n) j=1;else j++; } cnt=(i==1?0:n-i+1); top=0; fo(j,1,n) if (bz[j]&&R[j]>cnt) c[++top]=j; sort(c+1,c+top+1,cmpL); mx=0;mi=5*n; if (top) mi=min(mi,L[c[top]]*2+cnt); fd(j,top,1){ mx=max(mx,R[c[j]]); if (j>1) mi=min(mi,L[c[j-1]]*2+mx*2-cnt); } if (top) mi=min(mi,mx*2-cnt); if (!top) mi=cnt; num=mi; ans=min(ans,num+sum); cnt=i-1; top=0; fo(j,1,n) if (bz[j]&&L[j]>cnt) c[++top]=j; sort(c+1,c+top+1,cmpR); mx=0;mi=5*n; if (top) mi=min(mi,R[c[top]]*2+cnt); fd(j,top,1){ mx=max(mx,L[c[j]]); if (j>1) mi=min(mi,R[c[j-1]]*2+mx*2-cnt); } if (top) mi=min(mi,mx*2-cnt); if (!top) mi=cnt; num=mi; ans=min(ans,num+sum); } printf("%d\n",ans); }