题解 P4285 【[SHOI2008]汉诺塔】
提供一种打表新思路
先来证明一个其他题解都没有证明的结论:
(
感谢keytoyzi神仙的神仙思路
首先,在最初两层移动的时候,遵循的移动顺序规则是题中所给的顺序。
在
先把前
再对第
再进行某些操作(后文会展开);
最后所有盘子移动到
这等价于:
每一层对应一个新规则,把前
这个新规则是啥意思呢?光说理论太难以理解,上图:
解释一下:
再来两张状态转移的图:
(单箭头表示这一步操作优先级高于另一侧)
解释一下这张图。
刚开始对于前
由图中的递推公式,
对于第二种答案:
对于第三种答案:
这是一个线性表达式。
证毕。
所以,我们只需要知道移动一个盘子、两个盘子、三个盘子的情况,即可知道递推公式进而求解。
手动模拟打表,容易得到以下结果:
(
一个盘子:
两个盘子:
(1)AB>AC
①BC>BA ,ans[2]=3
②BC<BA ,ans[2]=5
(2)AB<AC
这里可以看做把
①ans[2]=3 :原BC>BA ,把BC 换了个位置后变成CB>CA
②ans[2]=5 :原BC<BA ,同理变成CB<CA
三个盘子:
(1)AB>AC
①BC>BA
(i)CB>CA ,ans[3]=9
(ii)CB<CA ,ans[3]=7
②BA>BC
ans[3]=17
(2)AB<AC
同理,不再赘述
下附递推AC代码:
#include<stdio.h>
char a[4];
int seq[3][3];
long long ans[40];
int main(){
int i,n;
scanf("%d",&n);
for(i=0;i<6;i++){
scanf("%s",a);
seq[a[0]-'A'][a[1]-'A']=6-i;
}
if(seq[0][1]>seq[0][2]){//AB>AC
if(seq[1][2]<seq[1][0]){//BC<BA
ans[2]=5;ans[3]=17;
}else{
if(seq[2][0]>seq[2][1]){//CA>CB
ans[2]=3;ans[3]=7;
}else{
ans[2]=3;ans[3]=9;
}
}
}else{//AB<AC
if(seq[2][1]<seq[2][0]){//CB<CA
ans[2]=5;ans[3]=17;
}else{
if(seq[1][0]>seq[1][2]){//BA>BC
ans[2]=3;ans[3]=7;
}else{
ans[2]=3;ans[3]=9;
}
}
}
ans[1]=1;
int b=(ans[2]*ans[2]-ans[1]*ans[3])/(ans[2]-ans[1]);
int k=(ans[2]-b)/cnt1;
for(i=4;i<=n;i++)ans[i]=ans[i-1]*k+b;
printf("%lld",ans[n]);
return 0;
}
其实,这已经没有必要写成递推形式了。我们在讨论三种答案的时候,其实已经可以手算算出三种情况的O(1)表达式了。
来一发最短AC代码
#include<stdio.h>
#include<math.h>
typedef long long ll;
char a[4];
int s[9],p,n,i=6;
ll f(int x){
if(x==1)return (ll)2*pow(3,n-1)-1;
if(x)return (ll)pow(2,n)-1;
return (ll)pow(3,n-1);
}
int main(){
scanf("%d",&n);
while(i--)scanf("%s",a),s[(a[0]-'A')*3+a[1]-'A']=i;
if(s[1]>s[2]){
if(s[5]<s[3])p=1;
else if(s[6]>s[7])p=2;
}else if(s[7]<s[6])p=1;
else if(s[3]>s[5])p=2;
printf("%lld",f(p));
return 0;
}