题目链接:https://vjudge.net/problem/UVA-10795
一道比较有思维含量的一道题:
注意一种分治的思想和“除了柱子x和柱子y之外的那个柱子”编号的问题。
首先在初始局面和目标局面中找到所在柱子不同的盘子中编号最大的一个,设为$k$,那么$k$必须要移动。
假设$k$要从柱子一移动到柱子二,在$k$移动之前,由于编号比$k$大的盘子不需要移动,也不会碍事,所以把它们当作不存在。编号比$k$小的只能在柱子三上。
那么这时柱子一上只有$k$,柱子二为空,柱子三上有$1,2,3...k-1$。这个局面成为“参考局面”。
而盘子的运动是可逆的,所以根据对称性,只要求出初始局面和目标局面移动到“参考局面”的步数和再加一即可。
那么写一个函数$f(P,i,final)$,($P[i]$表示盘子$i$的柱子编号,$f$表示把$1,2,3....i-1$全部移到柱子$final$所需步数)。
则$ans=f(start,k-1,6-start[k]-finish[k])+f(finish,k-1,6-start[k]-finish[k])+1$。
其中注意“除了柱子x和柱子y之外的那个柱子”编号为$6-x-y$。(普通汉诺塔结论)
最后进行递归:
$i=0 -> 0$
$P[i]=final -> f(P,i,final)=f(P,i-1,final)$
$f(P,i,final)=f(P,i-1,6-P[i]-final)+2^{i-1}$
AC代码:
1 #include<cstdio> 2 #include<iostream> 3 4 using namespace std; 5 typedef long long ll; 6 7 const int maxn=70; 8 int n,start[maxn],finish[maxn]; 9 10 ll f(int *P,int i,int final){ 11 if(i==0) return 0; 12 if(P[i]==final) return f(P,i-1,final); 13 return f(P,i-1,6-P[i]-final)+(1LL<<(i-1)); 14 } 15 16 int main(){ 17 int kase=0; 18 while(scanf("%d",&n)==1&&n){ 19 for(int i=1;i<=n;i++) scanf("%d",&start[i]); 20 for(int i=1;i<=n;i++) scanf("%d",&finish[i]); 21 int k=n; 22 while(k>=1&&start[k]==finish[k]) k--; 23 ll ans=0; 24 if(k>=1){ 25 int other=6-start[k]-finish[k]; 26 ans=f(start,k-1,final)+f(finish,k-1,final)+1; 27 } 28 printf("Case %d: %lld ",kase,ans); 29 } 30 return 0; 31 }