题目大意
给出ab01串,每次把a的一位取反,要求过程中不得有>2个连续的01相同
求把a变成b的最小次数
题解
普及组=集训队作业=比赛时8人AC
肝了一个上午
观察一下,把001变成011等价于什么
设相邻且相同的位置之间有一条边(也可看做滑块之类的),那么就等价于把00之间的边向右移一位,滑动过程中不能有边的端点重合
于是原问题就变成了有若干条边,一次可以把一条边移动一位,可以从头尾加边or从头尾移出去,求变成要求状态的最小步数
那就很简单了,枚举头删/加的边数,尾的删/加边可以算出来,最后方案就是 移出去的+从左往右一一对应的方案,这个直接对距离求和即可,因为按顺序来的话肯定存在一种方案满足不会端点相碰
注意ab的第一位要相等
题解做法是设01为红边,10为蓝边,移动类似,这里就不细讲了
时间复杂度O(n^2),感觉可以优化到O(n)
code
#include <bits/stdc++.h>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define abs(x) ((x)>0?(x):-(x))
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define ll long long
//#define file
using namespace std;
bool a[5001],b[5001];
int d1[5001],d2[5001],D[10001],n,N,i,j,k,l,s1,s2,ans,sum,tot;
char ch;
int main()
{
#ifdef file
freopen("agc030E.in","r",stdin);
#endif
scanf("%d",&n);N=n/2;
fo(i,1,n) {ch=getchar();while (ch!='0' && ch!='1') ch=getchar(); a[i]=ch=='1';}
fo(i,1,n) {ch=getchar();while (ch!='0' && ch!='1') ch=getchar(); b[i]=ch=='1';}
fo(i,1,n-1)
{
if (a[i]==a[i+1]) d1[++s1]=i;
if (b[i]==b[i+1]) d2[++s2]=i;
}
if (!s1 && !s2) {if (a[1]==b[1]) printf("0
"); else printf("%d
",n);return 0;}
ans=2133333333;
fo(i,-N,N)
if ((a[1]^(i&1))==b[1] && abs(s2-(s1+i))<=N)
{
j=s2-(s1+i);
tot=sum=0;
fo(k,1,-i) sum+=d1[k];
fo(k,1,i) D[++tot]=0;
fo(k,1+max(-i,0),s1-max(-j,0)) D[++tot]=d1[k];
fo(k,1,-j) sum+=n-d1[s1-k+1];
fo(k,1,j) D[++tot]=n;
if (tot>s2) continue;
fo(k,1,tot) sum+=abs(D[k]-d2[k]);
ans=min(ans,sum);
}
printf("%d
",ans);
fclose(stdin);
fclose(stdout);
return 0;
}