agc030E - Less than 3

题目大意

给出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\n"); else printf("%d\n",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\n",ans);
	
	fclose(stdin);
	fclose(stdout);
	
	return 0;
}

猜你喜欢

转载自www.cnblogs.com/gmh77/p/12799517.html