汉诺塔II
题目
分析
现在有四根柱子(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
题目
分析
这道题很简单。我们令将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
题目
分析
这道题是2064汉诺塔3的变形。我们分三步实现:
- 先将上面n-1个盘子从A-B,需要g(n-1)步
- 再将第n个盘子从A-B-C,需要2步
- 最后将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
题目
分析
这道题最水了。不要被表面假象所唬住。
假设有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;
}