题目链接:
http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1736
题目大意:
汉诺塔问题改版,求对于给定状态的汉诺塔问题,到指定目标状态,需要多少次操作
题目分析:
新汉诺塔问题:首先找最大不在目标柱子上的盘子K,因为如果最大的盘子在目标柱子上它不需要移动,也不碍事。
因此问题就成了把K移动到目标柱子,把1到(k-1)移动到中转柱子,所以假设K从A移动到B,A只有K,B是空的,C上面是K-1到1,把这个局面称为参考局面。因为移动是对称的,所以从参考局面移到目标局面与目标局面移到参考局面是一样的步数。
所以问题变成答案=从初始局面移到参考局面步数+目标局面移到参考局面步数+1;
需要写一个函数f(P,i,final),表示已知个盘子的初始柱面编号数组为P,把1到i移动到final的步数,本题答案是f(start,k-1,6-start[k]-finish[k])+f(finish,k-1,6-start[k]-finish[k])+1;
计算f(P,i,final),若p[i]=final,则f(P,i,final)=f(P,i-1,final);否则需要把前i-1个盘子挪到中转盘去,将盘子i移到柱子final去,做后把前i-1个盘子从中转盘移到柱子final.。最后一步是把i-1个盘子从一个柱子移到另一个柱子,根据旧汉诺塔问题,这个步骤需要2^(i-1)-1步,加上移动盘子i那一步,一共需要2^(i-1)步。
f(P,i,final)=f(P,i-1,6-p[i]-final)+2^(i-1);
代码:
#include "Task.h" #include<cstdio> long long f ( int *P, int i, int final ) { if ( i == 0 ) return 0; if ( P[i] == final ) return f( P, i - 1, final ); return f ( P, i - 1, 6 - P[i] - final ) + ( 1LL << ( i-1 ) ); } const int maxn = 60 + 10; int n, start[maxn], finash[maxn]; int main(){ int kase = 0; while(scanf("%d", &n) == 1 && n){ for(int i = 0; i <= n; i++) scanf("%d", start[i]); for(int i = 0; i <= n; i++) scanf("%d", finash[i]); int k = n; while (k >= 1 && start[k] == finash[k]) k--; long long ans = 0; if(k >= 1) { int other = 6 - start[k] - finash[k]; ans = f(start, k-1, other) + f(finash, k - 1, other) + 1; } printf("Case %d: %lld\n", ++kase, ans); } return 0; } Task::Task(void) { } Task::~Task(void) { }