题目:Balance
题意:
计数类DP。
给出一个长度为n的只由a,b,c组成的字符串,每次操作可以把一个字母变成其相邻的某个字母,定义一个字符串是平衡的当且仅当三个字母出现的次数相差不超过1,问这个字符串经过若干次操作可以变成多少个不同的平衡的字符串。
解析:
重点在于变化前后连续段之间的有序性(或者说相对位置)是不变的,所以只要保证枚举完所有的段,就可以保证不重不漏。
代码:
#include <bits/stdc++.h>
using namespace std;
const int mod=51123987;
int n,m,ans;
int now[3],f[155][55][55][55],nxt[155][3];
char ch[155];
inline bool check(int i,int j,int k)
{
return (i+j+k==n&&abs(i-j)<=1&&abs(i-k)<=1&&abs(j-k)<=1);
}
inline void add(int &a,int b)
{
a=(a+b)>=mod?(a+b)%mod:a+b;
}
int main()
{
scanf("%d%s",&n,ch+1),m=n/3+1;
for(int i=n;i;i--)
{
now[ch[i]-'a']=i;
for(int j=0;j<3;j++) nxt[i][j]=now[j];
}
f[1][0][0][0]=1;
for(int p=1;p<=n;p++)
for(int i=0;i<=m;i++)
for(int j=0;j<=m;j++)
for(int k=0;k<=m;k++)
{
if(check(i,j,k)) add(ans,f[p][i][j][k]);
if(nxt[p][0]) add(f[nxt[p][0]][i+1][j][k],f[p][i][j][k]);
if(nxt[p][1]) add(f[nxt[p][1]][i][j+1][k],f[p][i][j][k]);
if(nxt[p][2]) add(f[nxt[p][2]][i][j][k+1],f[p][i][j][k]);
}
cout<<ans;
return 0;
}