UVa10795

来源:互联网 发布:精神发育迟缓症状知乎 编辑:程序博客网 时间:2024/05/23 12:58

题目链接

简介:新汉诺塔问题(给出了初始状态和目标状态)

分析:
我们考虑最大的盘子,如果这个盘子初始状态和中止状态在一个柱子上,说明我们根本不用移动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 longusing 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\n",++cnt,ans);    }    return 0;}
原创粉丝点击