简介:新汉诺塔问题(给出了初始状态和目标状态)
分析:
我们考虑最大的盘子,如果这个盘子初始状态和中止状态在一个柱子上,说明我们根本不用移动ta
那么我们找到编号最大的需要移动是盘子k(初始状态所在的柱子和终止状态不一样)
假设k要从x—>y,那么在移动k之前三根柱子的状态一定是(我们可以忽略那些编号大于k且不需要移动的柱子):
x:k
y:空
z:1~k-1有序摆放
我们称这种状态为中间状态
由于盘子的移动是可逆的,根据对称性,
问题所需要的步数就是:(初始状态到达中间状态的步数)+(终止状态到达中间状态的步数)+1
汉诺塔问题有一个经典结论,不得不提一下:
把n个有序的盘子由一根柱子全部移动到另一根柱子上,所需的步数是:2^n-1
这就是一个递归的过程,
递归的柿子就可以表示为:f(start,i,final)=f(start,i-1,6-final-start[i])+f(finish,i-1,6-final-start[i])+1
start表示初始状态,finish表示终止状态,final表示需要移动到的柱子
我们把柱子依次编号1,2,3,那么除了初始状态和终止状态,剩下的那根柱子的编号就是6-final-finish[i]
f(P,now,final)表示当前的状态是P,我们需要把1~now个盘子全都移动到final上
怎么计算f(P,now,final)呢
如果当前的盘子now已经在目标柱子final上了,那f(P,now,final)=f(P,now-1,final)
如果不在目标柱子上,我们就要分成三步:
- 把now-1个盘子移动到(6-final-P[now])
- 把第now个盘子移动到final,需要一步
- 把now-1个盘子从(6-final-P[now])移动到final,注意这里是把now-1个盘子全部按顺序移动到final
根据汉诺塔问题的经典结论,这需要(2^(now-1)-1)步
所以f(P,now,final)=f(P,now-1,6-final-P[now])+2^(now-1)
//这里写代码片
#include<cstdio>
#include<cstring>
#include<iostream>
#define ll long long
using namespace std;
int n;
int start[103],finish[103];
ll f(int *P,int now,int final)
{
if (!now) return 0;
if (P[now]==final)
return f(P,now-1,final);
return f(P,now-1,6-final-P[now])+(1LL << (now-1));
}
int main()
{
int cnt=0;
while (scanf("%d",&n)!=EOF&&n)
{
for (int i=1;i<=n;i++) scanf("%d",&start[i]);
for (int i=1;i<=n;i++) scanf("%d",&finish[i]);
int i=n;
while (i>=1&&finish[i]==start[i]) i--;
ll ans=0;
if (i>=1)
{
int t=6-start[i]-finish[i];
ans=f(start,i-1,t)+f(finish,i-1,t)+1;
}
printf("Case %d: %lld
",++cnt,ans);
}
return 0;
}