题意
题解
显然由于有优先级存在,操作序列是唯一的。
我们设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;
}

本文介绍了一种新颖的方法来解决带优先级的汉诺塔问题。通过定义状态g[i][x]和f[i][x],文章详细阐述了状态转移的过程,并给出了具体的实现代码。
2759

被折叠的 条评论
为什么被折叠?



