Uva 10795 A Different Task

来源:互联网 发布:香港大学 专业 知乎 编辑:程序博客网 时间:2024/04/30 23:54

汉诺塔问题的变形,给定初始局面和目标局面,问最少多少步可以把初始局面变成目标局面。

 

找到初始位置和目标位置不同的最大盘子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;}

 

总结:汉诺塔类问题关键就是要找到一个会重叠出现的状态,然后写出递归式求解,中间也可以利用一些数学结论和技巧,比如可逆性。

0 0