HDU 汉诺塔 1207 2064 2077 1995

汉诺塔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 99999999

long 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;
}




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值