汉诺塔系列问题

HDU-1207 汉诺塔II

题目链接

https://vjudge.net/problem/HDU-1207

题面

Description

经典的汉诺塔问题经常作为一个递归的经典例题存在。可能有人并不知道汉诺塔问题的典故。汉诺塔来源于印度传说的一个故事,上帝创造世界时作了三根金刚石柱子,在一根柱子上从下往上按大小顺序摞着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

题解

我们设\(g[i]\)为借助2个柱子移动i个盘子到某个柱子上所花费的步数,\(f[i]\)为借助1个柱子移动i个盘子到某个柱子所花费的步数,那么我们要移动n个盘子到第四根柱上,首先要把k个盘子借助中间两个柱子移动到第4个柱子上,然后把n-k个柱子借助中间一个柱子移动到第4个柱子上,然后把k个盘子借助剩下的两个柱子移动到第4个柱子上,也就是\(g[n]=min(g[n],2\times g[k])+f[n-k](k=1...n-1)\),对于\(f[i]\)我们知道为\(2^i-1\),递推处理出\(g[i]\)就好了。

AC代码

#include <iostream>
#include <cstdio>
#include <cstring>
#define N 70
using namespace std;
long long f[N];
long long g[N];
int main() {
    f[0] = 0;
    for (int i = 1; i <= 63; i++) {
        f[i] = 2 * (f[i - 1] + 1) - 1;
    }
    long long inf = 0x7fffffffffffff;
    fill(g + 1, g + 66, inf);
    g[1] = 1;
    g[2] = 3;
    for (int i = 3; i <= 64; i++) {
        for (int k = 1; k < i; k++) {
            if (i == 64 && k == 1) continue;
            g[i] = min(g[i], 2 * g[k] + f[i - k]);
        }
    }
    int n;
    while (scanf("%d", &n) != EOF) {
        printf("%lld\n", g[n]);
    }
    return 0;
}

汉诺塔III

题目链接

https://vjudge.net/problem/HDU-2064

题面

Description

约19世纪末,在欧州的商店中出售一种智力玩具,在一块铜板上有三根杆,最左边的杆上自上而下、由小到大顺序串着由64个圆盘构成的塔。目的是将最左边杆上的盘全部移到右边的杆上,条件是一次只能移动一个盘,且不允许大盘放在小盘的上面。
现在我们改变游戏的玩法,不允许直接从最左(右)边移到最右(左)边(每次移动一定是移到中间杆或从中间移出),也不允许大盘放到下盘的上面。
Daisy已经做过原来的汉诺塔问题和汉诺塔II,但碰到这个问题时,她想了很久都不能解决,现在请你帮助她。现在有N个圆盘,她至少多少次移动才能把这些圆盘从最左边移到最右边?

Input

包含多组数据,每次输入一个N值(1<=N=35)。

Output

对于每组数据,输出移动最小的次数。

Sample Input

1
3
12

Sample Output

2
26
531440

题解

设移动k个圆盘从第一根柱子到第三根柱子需要\(f[k]\)次移动,则移动K-1个圆盘道第三根柱子需要\(f[k-1]\)次移动,再将最大的圆盘移动到中间柱子需要1次移动,然后将k-1个圆盘移动回第一根柱子同样需要\(f[k-1 ]\)次移动,移动最大的盘子到第三根柱子需要1次移动,最后将k-1个圆盘也移动到第三根柱子需要\(f[k-1]\)次移动,这样递归公式就是\(f[k]=3*f[k-1]+2\)。而递归的出口是k=1时,f[1]=2

AC代码

#include <iostream>
#include <cstdio>
#include <cstring>
#define N 50
using namespace std;
long long f[N];
int main() {
    int n;
    f[1] = 2;
    for (int i = 2; i <= 35; i++) {
        f[i] = f[i - 1] * 3 + 2;
    }
    while (scanf("%d", &n) != EOF) {
        printf("%lld\n", f[n]);
    }
    return 0;
}

HDU-1995 汉诺塔V

题目链接

https://vjudge.net/problem/HDU-1995

题面

Description

用1,2,...,n表示n个盘子,称为1号盘,2号盘,...。号数大盘子就大。经典的汉诺塔问
题经常作为一个递归的经典例题存在。可能有人并不知道汉诺塔问题的典故。汉诺塔来源于
印度传说的一个故事,上帝创造世界时作了三根金刚石柱子,在一根柱子上从下往上按大小
顺序摞着64片黄金圆盘。上帝命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱
子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一回只能移动一个圆盘。我们
知道最少需要移动2^64-1次.在移动过程中发现,有的圆盘移动次数多,有的少 。 告之盘
子总数和盘号,计算该盘子的移动次数.

Input

包含多组数据,首先输入T,表示有T组数据.每个数据一行,是盘子的数目N(1<=N<=60)和盘
号k(1<=k<=N)。

Output

对于每组数据,输出一个数,到达目标时k号盘需要的最少移动数。

Sample Input

2
60 1
3 1

Sample Output

576460752303423488
4

题解

由于比k小的盘子移动不会牵扯到k盘子的移动,所以问题转换为n-k+1阶汉诺塔第一个盘子的移动次数

对于n阶的汉诺塔,移动分为3步

  1. 将A上的n-1个盘子借助C移动到B
  2. 将A上的1个盘子移动到C
  3. 将n-1个盘子借助A移动到C

在第二步第一个盘子未动,所以第一个盘子的移动步数递推公式为\(f[i]=2*f[i-1]\),所以n阶汉诺塔第k个盘子的移动步数就为\(2^{n-k}\)

AC代码

#include <iostream>
#include <cstdio>
#include <cstring>
#define N 55
using namespace std;
long long f[N];
long long fpow(long long a, int b) {
    long long ans = 1;
    while (b) {
        if (b & 1) ans *= a;
        a *= a;
        b >>= 1;
    }
    return ans;
}
int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        int n, k;
        scanf("%d%d", &n, &k);
        printf("%lld\n", fpow(2, n - k));
    }
    return 0;
}

汉诺塔VIII(较难)

题目链接

https://vjudge.net/problem/HDU-2184

题面

Description

1,2,...,n表示n个盘子.数字大盘子就大.n个盘子放在第1根柱子上.大盘不能放在小盘上.在第1根柱子上的盘子是a[1],a[2],...,a[n]. a[1]=n,a[2]=n-1,...,a[n]=1.即a[1]是最下面的盘子.把n个盘子移动到第3根柱子.每次只能移动1个盘子,且大盘不能放在小盘上.问移动m次后的状况.

Input

第1行是整数T,表示有T组数据,下面有T行
每行2个整数n (1 ≤ n ≤ 63) ,m≤ 2^n-1

Output

输出移动m次后的状况.每组输出3行,第i行第1个数是第i根柱子上的盘子数,然后是盘子的号数.

Sample Input

3
3 2
4 5
39 183251937942

Sample Output

1 3
1 2
1 1
2 4 1
1 3
1 2
13 39 36 33 30 27 24 21 18 15 12 9 4 1
12 38 35 32 29 26 23 20 17 14 11 8 5
14 37 34 31 28 25 22 19 16 13 10 7 6 3 2

题解

我们知道移动n阶汉诺塔需要\(2^n-1\)步,则移动n-1个盘子到b盘需要\(2^{n-1}-1\)步,移动最大的盘子到c盘需要1步,移动n-1个盘子到c盘需要\(2^{n-1}-1\),然后我们可以递归处理移动m次盘子时,每个盘子的状况

首先从第n个盘子,移动m次开始处理,有以下三种情况

  1. 如果\(m>2^{n-1}\),那么第n个盘子肯定在c柱上,我们就先把第n个盘子放到c柱上,花费\(2^{n-1}\)步,剩下的n-1个盘子剩下\(m-2^{n-1}\)步,借助a柱移动到c柱上
  2. 如果\(m=2^{n-1}\)那么正好是第n个盘子在c柱上,其他均在b柱上,直接记录进数组,不再递归
  3. 如果\(m<2^{n-1}\),说明第n个盘子只能在a柱上,用m步移动剩下的n-1个盘子,借助c柱移动到b柱

递归结束后,每个柱子上有哪些盘子就被确定了

AC代码

#include<bits/stdc++.h>
#define N 70
using namespace std;
typedef unsigned long long ll;
ll a[N], b[N], c[N];
ll pow2[N];
void dfs(int n, ll m, ll a[], ll b[], ll c[]) {
    if (m > pow2[n - 1]) {
        c[++c[0]] = n;
        dfs(n - 1, m - pow2[n - 1], b, a, c);
    }
    else if (m == pow2[n - 1]) {
        c[++c[0]] = n;
        for (int i = n - 1; i >= 1; i--) {
            b[++b[0]] = i;
        }
    }
    else {
        a[++a[0]] = n;
        dfs(n - 1, m, a, c, b);
    }
}
int main() {
    pow2[0] = 1;
    for (int i = 1; i <= 63; i++) {
        pow2[i] = pow2[i - 1] * 2;
    }
    int t;
    scanf("%d", &t);
    while (t--) {
        a[0] = b[0] = c[0] = 0;
        ll n, m;
        scanf("%llu%llu", &n, &m);
        dfs(n, m, a, b, c);
        printf("%llu", a[0]);
        for (int i = 1; i <= a[0]; i++) {
            printf(" %llu", a[i]);
        }
        printf("\n%llu", b[0]);
        for (int i = 1; i <= b[0]; i++) {
            printf(" %llu", b[i]);
        }
        printf("\n%llu", c[0]);
        for (int i = 1; i <= c[0]; i++) {
            printf(" %llu", c[i]);
        }
        printf("\n");
    }
    return 0;
}

原文地址:https://www.cnblogs.com/artoriax/p/10386202.html

时间: 2024-11-01 20:29:18

汉诺塔系列问题的相关文章

汉诺塔系列问题: 汉诺塔II、汉诺塔III、汉诺塔IV、汉诺塔V、汉诺塔VI、汉诺塔VII

汉诺塔II:(hdu1207) /先说汉若塔I(经典汉若塔问题),有三塔,A塔从小到大从上至下放有N个盘子,如今要搬到目标C上, 规则小的必需放在大的上面,每次搬一个,求最小步数.这个问题简单,DP:a[n]=a[n-1]+1+a[n-1],先把 上面的n-1个放在B上,把最大的放在目标C上,再把N-1个放回到C上就可以. </pre><p></p>如今是汉若塔II,改为四个塔,開始方程想简单了,不是最优的.给出网上的一种最优解法例如以下:(1)将x(1<=x&l

汉若塔系列续:汉诺塔VIII、汉诺塔IX、汉诺塔X。

汉诺塔VIII,在经典汉若塔问题上.问n个盘子的情况下,移动m次以后,是什么状态. (与第七代互为逆命题) 我的思路:本质还是dfs,可是用m的值来指引方向,每搜一层确定第i个盘子在哪个塔,o(n)的算法,看图说明: #include<iostream> #include<vector> using namespace std; char get[65]; //记录I号盘子在哪个塔 long long f[65]; //2^i次的值 void got() //预处理的f[i]; 注

Algorithm系列之汉诺塔(Hanoi)

作者:i_dovelemon 来源:CSDN 日期:2014 / 11 4 主题: 递归, Hanoi 引言 在算法的世界里,有递归这样一个强大的工具.很多的算法,我们都可以使用递归来实现,并且非常的简单,容易理解.今天,我们就来讲解一个使用递归解决的问题.著名的汉诺塔问题,就能够使用递归算法来解决.我们先来了解汉诺塔问题是什么: 汉诺塔问题最早由一个法国数学家卢卡斯在1890年发表的,当时的版本是这样的,当64个圆盘被从梵塔上移走的时候,世界末日也就来临了.如果祭司每一分钟移动一个圆盘,请估计

汉诺塔VII(递推,模拟)

汉诺塔VII Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 1503 Accepted Submission(s): 1077   Problem Description n个盘子的汉诺塔问题的最少移动次数是2^n-1,即在移动过程中会产生2^n个系列.由于发生错移产生的系列就增加了,这种错误是放错了柱子,并不会把大盘放到小盘上,即各柱子从

HDU 1997 汉诺塔VII

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1997 Problem Description n个盘子的汉诺塔问题的最少移动次数是2^n-1,即在移动过程中会产生2^n个系列.由于发生错移产生的系列就增加了,这种错误是放错了柱子,并不会把大盘放到小盘上,即各柱子从下往上的大小仍保持如下关系 : n=m+p+qa1>a2>...>amb1>b2>...>bpc1>c2>...>cqai是A柱上的盘的盘号系

HDU2.2.3 (汉诺塔VII)

汉诺塔VII Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 1574 Accepted Submission(s): 1123   Problem Description n个盘子的汉诺塔问题的最少移动次数是2^n-1,即在移动过程中会产生2^n个系列.由于发生错移产生的系列就增加了,这种错误是放错了柱子,并不会把大盘放到小盘上,即各柱子从

HDU1996 汉诺塔VI 【递推】

汉诺塔VI Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 1374    Accepted Submission(s): 984 Problem Description n个盘子的汉诺塔问题的最少移动次数是2^n-1,即在移动过程中会产生2^n个系列.由于 发生错移产生的系列就增加了,这种错误是放错了柱子,并不会把大盘放到小盘上,即各

HDU 1996 汉诺塔VI

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1996 n个盘子的汉诺塔问题的最少移动次数是2^n-1,即在移动过程中会产生2^n个系列.由于 发生错移产生的系列就增加了,这种错误是放错了柱子,并不会把大盘放到小盘上,即各柱 子从下往上的大小仍保持如下关系 : n=m+p+q a1>a2>...>am b1>b2>...>bp c1>c2>...>cq 计算所有会产生的系列总数. Input包含多组数据,

汉诺塔VI

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 1834    Accepted Submission(s): 1279 Problem Description n个盘子的汉诺塔问题的最少移动次数是2^n-1,即在移动过程中会产生2^n个系列.由于 发生错移产生的系列就增加了,这种错误是放错了柱子,并不会把大盘放到小盘上,即各柱 子从下