问题描述
四柱汉诺塔,在A柱上有N个盘子,最少经过多少次移动能把盘子全部移动到D上?
将盘子人为分成上下两个部分,上面部分是n-k个盘子,下面部分是k个盘子。那么想要把总计n个盘子从A柱挪到D柱就相当于:
通过CD柱把上面n-k个盘子挪到B柱
+通过C柱把下面k个盘子挪到D柱
+通过AC柱把B上面的n-k个盘子挪到D柱
我们假设四柱挪n-k盘子的步数需要f[n-k]
又,我们熟悉三柱汉诺塔挪动n个盘子需要步数2^n-1
因此,对于每一个k,我们有:
long long temp=2*f[x-k]+pow(2,k)-1
此时我们只需要对每一个k进行枚举,得到使得f[n]取到最小值的k,并得到f[n]即可
可以通过如下代码片段实现:
f[0]=0;
f[1]=1;
f[2]=3;
for(int x=3;x<=n;x++){
maxn=MAX;
for(int k=1;k<=x;k++){
long long temp;
temp=2*f[x-k]+pow(2,k)-1;
if(temp<maxn){
maxn=temp;
}
}
f[x]=maxn;
}
整体代码如下:
#include<stdio.h>
#include<math.h>
#define MAX 1000000
int main(){
int n;
int f[100]={0};
int maxn;
scanf("%d",&n);
f[0]=0;
f[1]=1;
f[2]=3;
for(int x=3;x<=n;x++){
maxn=MAX;
for(int k=1;k<=x;k++){
long long temp;
temp=2*f[x-k]+pow(2,k)-1;
if(temp<maxn){
maxn=temp;
}
}
f[x]=maxn;
}
printf("%d",f[n]);
}
当我们输入数据测试,我们可以发现在n值大于等于63时,会有如下结果:
其原因是long long类型最多可以存放64字节的数据,当数据过大,数据发生溢出。
可是在n=62时,f[n]的数值根本就不大,因此此处还有很大的优化空间。我们通过循环输入,找其中的规律,代码如下:
#include<stdio.h>
#include<math.h>
#define MAX 1000000
int main(){
int n;
int f[100]={0};
int maxn;
f[0]=0;
f[1]=1;
f[2]=3;
for(int x=3;x<=63;x++){
maxn=MAX;
for(int k=1;k<=x;k++){
long long temp;
temp=2*f[x-k]+pow(2,k)-1;
if(temp<maxn){
maxn=temp;
}
}
f[x]=maxn;
}
while(~scanf("%d",&n)){
printf("%d\n",f[n]);
}
}
测试各组数,寻找规律:
n每增加1,f[n]的值增加2^i
其中i以j为周期逐次加一
每个周期结束后j也会加一
因此我们根据规律,可以写出运行速度更快的优化后的代码如下:
#include<stdio.h>
#include<math.h>
int main(){
int n,i,j,k=0,t=0;
long long output[100]={0};
for(int a=1;a<=100;a++){
for(i=1;i*(i+1)/2<a;i++){
}
output[a]=output[a-1]+pow(2,i-1);
}
while(scanf("%d",&k)!=EOF){
printf("Case #%d: %lld\n",k,output[k]);
}
}
此种算法适用于更大数字的汉诺dd塔问题,但是也有计算的极限,可以通过大数字的逐位计算解决,实现起来也非常简单,这里不做赘述