HDU 汉诺塔 1207 2064 2077 1995

来源:互联网 发布:什么是软件系统 编辑:程序博客网 时间:2024/05/19 09:38

汉诺塔II

题目

点击打开链接1207

分析

现在有四根柱子(A,B,C,D),其他规则不变。我们的目标是将n个盘移到D上,所以我们首先将n-1个盘移到B、C上,然后将第n个盘移到D上。我们令从A->D需要F(n)步,下面分为三步实现:

  • 将r个柱子从A->B,共有F(r)步;
  • 将剩下的n-r个柱子从A-D,此时,B柱上所有的盘子都比这n-r个盘子小,所以只能使用C、D,这是一个经典的汉诺塔问题。需要2^(n-r)-1步
  • 再将B柱上的r个盘子移到D上,需要F(r)步

所以一共需要F(n) = 2*F(r) + 2^(n-r) - 1步。对于不同的r,F(n)不同。我们采用循环的方式,对r进行遍历,用min标记F(n)最小值。求得min[ 2*F(r) + 2^(n-r) - 1] (1 <= r < n)

代码

#include <stdio.h>#include <math.h>#define MAX 70 #define INF 99999999long long hanoi2(int);int main(){    int n;     long long res;    while (scanf("%d", &n) != EOF)    {        if (n < 1 || n > 64)            break;        res = hanoi2(n);        printf("%lld\n", res);    }    return 0;}long long hanoi2(int n){    long long  a[MAX] = {        0, 1, 3    };    long long min;    int i, j;    for (i = 3; i <= n; i++)    {        min = INF; //作为哨兵,初始化应该放在第一个循环内,这样第二个循环才能顺利执            for (j = 1; j < i; j++)        {            /* 一定要转换为unsigned,否则当n=64时,结果会溢出 */            if(min > 2*a[j] + (unsigned long long )pow(2, i-j) - 1)                min = 2*a[j] + (unsigned long long )pow(2,i-j) - 1;        }        a[i] = min;    }    return a[n];    }

汉诺塔III

题目

点击打开链接 2064

分析

这道题很简单。我们令将n个盘从A->B需要F(n)步。下面分三步进行:

  • 将n-1个盘从A-B-C,需要F(n-1)步
  • 将第n个盘从A-B,再将n-1个盘从C-B-A,需要F(n-1) + 1 步
  • 第n个盘B-C,再将n-1个盘从A-B-C,需要1 + F(n-1) 步

所以F(n) = 3*F(n-1) + 2步,有通项公式 f(n) = 3^n - 1

代码


int main(){int n, i;long long res;while (scanf("%d", &n) != EOF){if (n < 1 || n > 35)break;res = 2;if (n == 1)printf("%lld\n", res);else{for (i = 2; i <= n; i++){res = 3*res + 2;}printf("%lld\n", res);}}return 0;}

汉诺塔IV

题目

点击打开 2077

分析

这道题是2064汉诺塔3的变形。我们分三步实现:

  1. 先将上面n-1个盘子从A-B,需要g(n-1)步
  2. 再将第n个盘子从A-B-C,需要2步
  3. 最后将n-1个盘子从B-C,需要g(n-1)步

F(n) = 2 * g(n-1) + 2.所以题目转化为只要求g(n)的递推式,或者通项公式即可。

为了实现1,我们需要将上面n-2个盘子从A-B-C(这就是汉诺塔3的问题),再将第n-1个盘子从A-B。然后,再将上面n-2个盘子从C-B。有,g(n-1) = 3^(n-2)-1 + 1 + g(n-2) ,从而有g(n) = (3^n - 1)/2。

所以,F(n) = 3^(n-1) + 1

代码

#include <stdio.h>#include <math.h>int main(){int n, t;long long res, temp;while (scanf("%d", &t) != EOF){while(t--){scanf("%d", &n);if (n < 1 || n > 20)break;res = (long long) pow (3, n - 1) + 1;printf("%lld\n", res);}}return 0;}

汉诺塔V

题目

点击打开1995

分析

这道题最水了。不要被表面假象所唬住。

假设有64个盘子,我们先看第64个盘子,它只需要移动1次;第63个盘子移动两次,前一次是为地64个盘子移的;第62个盘子移动4次,前两次是为第64个盘子移的,第三次是为了第63个盘子移的;。。。

可以看出规律第k个盘子移动了 2^(n-k)次。

代码

#include <stdio.h>#include <math.h>int main(){int t, n, k;long long res;while (scanf("%d", &t) != EOF){while (t--){scanf("%d %d", &n, &k);if ( k >= 1 && k <= n && n <= 60){res = (long long) pow (2, n-k);printf("%lld\n", res);}}}return 0;}