一、汉诺塔问题
小故事:大梵天创造世界的时候做了三根金刚石柱子(A,B,C),在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。
在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。
64根柱子移动完毕之日,就是世界毁灭之时。
分析:当有两个圆盘时,因为小的圆环上面不能放大的,所以小圆环不能先移到目标柱上,否则大的放不了,那么就先把它移动到缓冲柱上,一步就搞定了,接下来把大的移到目标柱上,最后把小的移到目标柱上,最少移动次数为3次。
这三个步骤在下述中称为基础步骤。
三个圆盘的情况:
为了把1,2,3号圆盘挪入塔3(即目标地)可先将1,2号看作一块,即:先将1,2号放入缓冲地,三号放入目标地,最后将1,2号放入目标地。
接下来的问题是如何将1,2号放入缓冲地,而这只需执行上述两个圆盘情况下的步骤(即:基础步骤)。
步骤3 | 3号 | 2号,1号 | |
步骤2 | 3号 | 2号 | 1号 |
步骤1 | 3号,2号 | 1号 | |
塔1 | 塔2 | 塔3 |
最后执行基础步骤将1,2号从缓冲地挪入目标地。
步骤3 | 3号,2号,1号 | ||
步骤2 | 1号 | 3号,2号 | |
步骤1 | 1号 | 2号 | 3号 |
塔1 | 塔2 | 塔3 |
三个圆盘的问题已经解决,那四个圆盘问题的思路同理:
将上三个圆盘当作一个整体,执行基础步骤,而为了移动上三个圆盘到相应位置,应将上两个圆盘看作一个整体执行基础步骤,继续递推至两个圆盘的情况去执行基础步骤。接下来就是回归:即,现实中你要完成这个工程要走的具体每一步(就是,要怎么走,下一步要走哪一个圆盘到哪去)。
综上所述:可推广至n个圆盘的走法:将上(n-1)个圆盘看做整体,再将上(n-2)个圆盘作为一个整体.......依次递推至两个圆盘的走法:于是可得递推公式:
f(n)=f(n-1)+1+f(n-1)
(即:将(n-1)个圆盘移至缓冲地的步数+第n个圆盘移至目标地的步数+将(n-1)个圆盘移至目标地的步数)
如果递推至两个圆盘的情况的时候,那么可以得到回归条件f(2)=3(基础步骤步数)。
因此:
首先实现最少需要多少步的算法:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int Hnt(int n) {
if (n == 2)
return 3;
return (2 * Hnt(n - 1) + 1);
}
int main() {
int n = 0;
scanf("%d", &n);
printf("最少需要%d步", Hnt(n));
}
再进行代码实现具体的每一步:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<malloc.h>
void PutIn(int* arrForm, int* arrTo, int leng) {
int i = leng - 1;
int disk = 0;
while (1) {
if (arrForm[i] != 0) {
disk = arrForm[i];
arrForm[i] = 0;
break;
}
i--;
}
i = 0;
while (1) {
if (arrTo[i] == 0) {
arrTo[i] = disk;
break;
}
i++;
}
}
void swap(int* arr[3],int leng) {
int* p;
for (int i = 0; i < 2; i++)
for (int j = 0; j <2-i; j++)
if (arr[j][leng] > arr[j+1][leng])
p = arr[j], arr[j] = arr[j+1], arr[j+1] = p;
}
void PrintArr(int* a, int* b, int* c, int leng) {
int n = leng;
int* p = NULL;
int* arr[3] = { a,b,c };
swap(arr,leng);
for (int i = n - 1; i >= 0; i--)
printf("--%d: %d----%d----%d----\n", i + 1, arr[0][i], arr[1][i], arr[2][i]);
printf("-----A----B----C----\n\n");
}
void Hnt(int n, int* a, int* b, int* c, int leng) {
if (n == 2) {
PutIn(a, b, leng);
PrintArr(a, b, c, leng);
PutIn(a, c, leng);
PrintArr(a, b, c, leng);
PutIn(b, c, leng);
PrintArr(a, b, c, leng);
}
else {
Hnt(n - 1, a, c, b, leng);
PutIn(a, c, leng);
PrintArr(a, b, c, leng);
Hnt(n - 1, b, a, c, leng);
}
}
int main() {
int n = 0;
scanf("%d", &n);
int* A = (int*)malloc(sizeof(int) * (n+1));
int* B = (int*)malloc(sizeof(int) * (n+1));
int* C = (int*)malloc(sizeof(int) * (n+1));
for (int i = 0; i < n; i++)
B[i] = C[i] = 0, A[i] = n - i;
A[n] = 1, B[n] = 2, C[n] = 3;
int leng = n;
Hnt(n, A, B, C, leng);
}
汉塔诺问题已然完毕。
接下来第二个问题,兔子繁殖:
一对兔子从出生后的第三个月起,每个月都生一对兔子,小兔子长到第三个月后,每个月又生一对兔子。假设所有的兔子都不死,问30个月内每个月的兔子总数为多少?
先对问题进行分析:
兔子每生下来等三个月才能开始生育,
所以假设某月有M个兔子,那么接下来的第三个月这M个兔子皆成年。
因此第n个月增加的兔子数目是第(n-3)个月的兔子数目(即:所有成年兔子所生育的兔子数目)与上月(n-1月)兔子的数目的和(即:新生兔子+原生兔子)。
代码实现:
#include<stdio.h>
long int RabbitBorn(int time,long int* number)
{ int num;
if (time <= 3) {
num = 1;
number[time-1] = num;
}
else
if (number[time-1]==0) {
num = RabbitBorn(time - 3,number) + RabbitBorn(time - 1,number);
number[time-1] = num;
}
else
num = number[time - 4] + number[time - 2];
return num;
}
int main() {
long int num[30] = { 0 };
int time = 30;
printf("%ld\n", RabbitBorn(time, num));
for (int i = 0; i < 30; i++) {
printf("第%d个月:%ld\n", i,num[i]);
}
}
经以上分析回归问题的重点是写出回归方程组,即数学表达式,递推公式。