http://acm.hdu.edu.cn/showproblem.php?pid=1207
经典的汉诺塔问题经常作为一个递归的经典例题存在。可能有人并不知道汉诺塔问题的典故。汉诺塔来源于印度传说的一个故事,上帝创造世界时作了三根金刚石柱子,在一根柱子上从下往上按大小顺序摞着64片黄金圆盘。上帝命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一回只能移动一个圆盘。有预言说,这件事完成时宇宙会在一瞬间闪电式毁灭。也有人相信婆罗门至今仍在一刻不停地搬动着圆盘。恩,当然这个传说并不可信,如今汉诺塔更多的是作为一个玩具存在。Gardon就收到了一个汉诺塔玩具作为生日礼物。
Gardon是个怕麻烦的人(恩,就是爱偷懒的人),很显然将64个圆盘逐一搬动直到所有的盘子都到达第三个柱子上很困难,所以Gardon决定作个小弊,他又找来了一根一模一样的柱子,通过这个柱子来更快的把所有的盘子移到第三个柱子上。下面的问题就是:当Gardon在一次游戏中使用了N个盘子时,他需要多少次移动才能把他们都移到第三个柱子上?很显然,在没有第四个柱子时,问题的解是2^N-1,但现在有了这个柱子的帮助,又该是多少呢?
Input
包含多组数据,每个数据一行,是盘子的数目N(1<=N<=64)。
Output
对于每组数据,输出一个数,到达目标需要的最少的移动数。
Sample Input
1
3
12
Sample Output
1
5
81
思路:用f[i]表示经典汉诺塔问题的结果,F[i]表示有四个柱子的汉诺塔问题的结果,很明显,四个柱子时候所需要的步数更少。所以当计算F[i]时,开一个从1到i的循环(1<=j<i),首先把上面j个盘子移出去,步数是F[j],因为此时共四个柱子,然后把底部生于的i-j个盘子移到第三个柱子上,步数是f[i-j],因为这个时候只有三个柱子了。(放好的那个柱子不能用了)最后再把原来摆好的放到第三个柱子上,步数是F[j],理由同上。因此F[i]=min(F[i],2*F[j] + f[i-j])。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<set>
#include<algorithm>
#include<iterator>
#define INF 0x3f3f3f3f
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
ull f[70]; //三个柱子的汉诺塔
ull F[70]; //四个柱子的汉诺塔
//考虑先把上面的j个放到某个柱子上 F[j]
//再考虑把剩下的i-j个放到第三个柱子上 f[i-j]
//再把那个柱子上的移过来 F[j]
//总:2*F[j]+f[i-j]; 遍历取最小的
int main()
{
f[1]=1;
for(int i=2;i<=64;i++)
f[i]=2*f[i-1]+1;
F[1]=1;
F[2]=3;
F[3]=5;
for(int i=4;i<=64;i++)
{
F[i]=f[i];
for(int j=1;j<i;j++)
if(2*F[j]+f[i-j]<F[i])
F[i]=2*F[j]+f[i-j];
}
int n;
while(~scanf("%d",&n))
printf("%llu\n",F[n]);
return 0;
}