Uva 10795 A Different Task
汉诺塔问题的变形,给定初始局面和目标局面,问最少多少步可以把初始局面变成目标局面。
找到初始位置和目标位置不同的最大盘子X,那么比这个盘子还大的盘子就可以无视掉了。因为他们既不会被移动也不需要被移动。
我们设f(p[],x,final)为当前初始状态为p,要把比x小的盘子移动到柱子final的最少步数那么有f(p[],x,final)=f(p[],x-1,6-final-p[x])+2^(x-1)-1+1
意思就是先把比x-1小的都移动到柱子6-final-p[x],然后把x-1移动到final,然后把比x-1小的盘子移动到final
有了这个式子之后,我们要求的答案可以分解成,从开始局面开始,把比X小的盘子全部移动到中转位置,然后移动X,然后在移动到目标局面即可。最后移动到目标局面可以看成是从目标局面移动到中转位置,那么答案就是
ans=f(start[],X,6 – start[X] – final[X]) + f(final[],X,6 – start[X] – final[X]) + 1
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> using namespace std; typedef long long LL; const int maxn = 70; int start[maxn],finish[maxn]; LL f(int P[],int i,int fin) { if(i == 0) return 0; if(P[i] == fin) return f(P,i - 1,fin); return f(P,i - 1,6 - P[i] - fin) + 1 + (1LL << (i - 1)) - 1; } int main() { int n,kase = 1; while(scanf("%d",&n),n) { LL ans = 0LL; for(int i = 1;i <= n;i++) { scanf("%d",&start[i]); } for(int i = 1;i <= n;i++) { scanf("%d",&finish[i]); } for(int i = n;i >= 1;i--) if(start[i] != finish[i]) { ans = f(start,i - 1,6 - start[i] - finish[i]) + f(finish,i - 1,6 - start[i] - finish[i]) + 1; break; } cout << "Case " << kase << ": " << ans << endl; kase++; } return 0; }
总结:汉诺塔类问题关键就是要找到一个会重叠出现的状态,然后写出递归式求解,中间也可以利用一些数学结论和技巧,比如可逆性。