题意
题解
显然由于有优先级存在,操作序列是唯一的。
我们设
g[i][x]
表示一开始有i个盘在x柱,经过一系列操作,他们都到了哪个柱子上。这是唯一确定的。我们再设这个过程的操作方案数为
f[i][x]
。
这样
f[n][0]
就是答案了。(我们用0,1,2表示柱A,B,C)
如何递推呢?联系经典的汉诺塔递推,i是由i-1得来的,我们同样用这样的思路来分析。
假设我们现在已经求出了
f[i−1][0/1/2]
和
g[i−1][0/1/2]
,要得到
f[i][x]
:
若在1~i-1盘下面多加一个i盘,动i盘之前的操作的是不会变的。
因为i盘是最大的,只有
f[i−1][x]
次操作做完之后才能有空柱子使i盘能移动。
由于此时1~i-1已经到
g[i−1][x]
柱上了,所以i盘必然被移到
3−x−g[i−1][x]
柱上。
为了方便描述,我们设y=
g[i−1][x]
,z=
3−x−g[i−1][x]
。
现在1~i-1在y上,i在z上。
然后,由于有限制(2),所以i盘不会动,必然是1~i-1盘进行移动。没有整体移动完之前i盘也不会动,理由和一开始相同。
这里就需要讨论y柱上的1~i-1盘会被移动到哪里。
若
g[i−1][y]=z
:
移完之后1~i都到z上了,得
g[i][x]=z,f[i][x]=f[i−1][x]+1+f[i−1][y]
。
若
g[i−1][y]=x
:
先是1~i-1到了x柱上,然后i盘移动到y上,再由于
g[i−1][x]
为y,所有的都到y柱上了。
得
g[i][x]=y,f[i][x]=f[i−1][x]+1+f[i−1][y]+1+f[i−1][x]
。
这下状态转移就解决了。
初始化
f[1][0/1/2]=1
,
g[1][0/1/2]
根据给出的优先级即可得到。
还是很妙的。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn=55;
int n,g[maxn][3];
char s[10][5];
LL f[maxn][3];
int main(){
freopen("bzoj1019.in","r",stdin);
freopen("bzoj1019.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=6;i++) scanf("%s",s[i]);
for(int i=6;i>=1;i--) g[1][s[i][0]-'A']=s[i][1]-'A';
f[1][0]=f[1][1]=f[1][2]=1;
for(int i=2;i<=n;i++)
for(int x=0;x<=2;x++){
int y=g[i-1][x],z=3-y-x;
if(g[i-1][y]==z) g[i][x]=z, f[i][x]=f[i-1][x]+f[i-1][y]+1;
else g[i][x]=y, f[i][x]=2*f[i-1][x]+f[i-1][y]+2;
}
printf("%lld\n",f[n][0]);
return 0;
}