题目:https://vjudge.net/problem/UVA-10795
新汉诺塔问题往旧汉诺塔问题上转换,所以先来看旧汉诺塔问题。
旧汉诺塔:
汉诺塔问题是一个经典的问题。汉诺塔(Hanoi Tower),又称河内塔,源于印度一个古老传说。 大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。
大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。
并且规定,任何时候,在小圆盘上都不能放大圆盘,且在三根柱子之间一次只能移动一个圆盘。
问应该如何操作?
既然是个递推问题,那就从大到小的依次找圆盘,若想把第n个圆盘放到第i根柱子上,首先保证,第i根柱子上没有比n小的盘子,
且n上面也无任何圆盘,所以这时候才能把n放到第i根柱子上。依次类推到第1个盘子。
在写递推函数的时候要逆着写,不要顺着往下想,比如在考虑n的情况的时候,要先想第n-1个情况已经完成了,这时候n该干什么了,这个函数的是为n-1后n的情况进行的治理,所以也是分治情况的一种。
所以该这样实现:
#include<bits/stdc++.h>
using namespace std;
void hannuo(int n,char first,char middle,char end){
if(n<1)
return ;
hannuo(n-1,first,end,middle);
printf("把%d从%c放入到了%c\n",n,first,end);
hannuo(n-1,middle,first,end);
}
int main(){
int n;
printf("有多少个盘子\n");
scanf("%d",&n);
hannuo(n,'a','b','c');
}
再来看新汉诺塔问题,由于一开始情况未知,所以要往旧汉诺塔问题上转换,
初始局面和目标局面移动到参考局面的和加1就是结果。
参考局面:要移动的编号为n的盘子单独在一根柱子上,其他的棋盘全都在除了n所在和n目标柱子的柱子上。
所以f(start,n-1,other)表示初始局面到参考局面,
参考局面到目标局面是针对于第n-1个盘子来说的,因为移动可逆,也可以想成是目标局面到参考局面,这么想只是为了
函数更好的实施。
所以目标局面到参考局面f(finish,n-1,other).
这个时候每个单独的f(),就转换成了旧汉诺塔问题,而旧汉诺塔问题中,每把n个柱子移动到另一个柱子上,需要的
步是2^i-1步。
这个问题就因此解决了
实现:
#include<bits/stdc++.h>
using namespace std;
const int maxn=65;
typedef long long LL;
int n;
int start[maxn],finish[maxn];
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));
}
int main(){
int kase=0;
while(scanf("%d",&n) && n){
for(int i=1;i<=n;i++) scanf("%d",&start[i]);
for(int i=1;i<=n;i++) scanf("%d",&finish[i]);
int k=n;
while(k>=1 && start[k]==finish[k]) k--;
long long ans=0;
if(k>=1){
int other=6-start[k]-finish[k];
ans=f(start,k-1,other)+f(finish,k-1,other)+1;
}
printf("Case %d: %lld\n",++kase,ans);
}
return 0;
}