首页 > 其他分享 >汉诺塔(河内塔)题解

汉诺塔(河内塔)题解

时间:2023-10-09 21:24:34浏览次数:33  
标签:圆盘 题解 复杂度 long int 汉诺塔 ull 移动 河内

汉诺塔(河内塔)题解

我们定义 \(T_n\) 为根据规则将 \(n\) 个圆盘从一根柱子移动到另一根柱子的最少移动步数,按照这样的定义,本道题的答案实际上就是 \(T_n\)。

通过手动模拟,可得到 \(T_1=1,T_2=3\)。同时显然有 \(T_0=0\),即表示 \(0\) 个圆盘根本无需做任何移动。

接着我们开始考虑大的情形:怎样才能移动一个大的塔呢?通过移动 \(3\) 个圆盘的试验发现,得先将前两个小的圆盘移动到第二根柱子上,再将最大的那个圆盘移到第三根柱子上,最后把那两个小圆盘移到最大的圆盘上面。

这给了我们启发:首先将 \(n-1\) 个小的圆盘移动到第二根柱子上(需要 \(T_{n-1}\) 次移动),再将最大的圆盘移动到第三根柱子上(需要 \(1\) 次移动),最后把 \(n-1\) 个圆盘移回最大的圆盘上面(再需要 \(T_{n-1}\) 次移动)。这样,就需要 \(2T_{n-1}+1\) 次移动就能移动 \(n\) (\(n>0\))个圆盘了,发现可以对子问题进行求解,故采用递推(递归)的形式。

用式子表示即:\(T_n=2T_{n-1}+1\),这样可以\(O(n)\) 的时间复杂度通过。

#include<bits/stdc++.h>
#define ull unsigned long long 
using namespace std;
ull n,T[65];
int main(){cin>>n;
	for(int i=1;i<=n;i++)T[i]=2*T[i-1]+1;
	cout<<T[n];
}

我们会注意到 \(T_3=7,T_4=15,T_5=31,T_6=63\)。

发现 \(7=2^3-1,15=2^4-1,31=2^5-1,63=2^6-1\)。

所以我们假设 \(T_n=2^n-1\),通过数学归纳法证明得出 \(T_n=2T_{n-1}+1=2(2^{n-1}-1)+1=2^n-1\)。

所以又有如下代码,时间复杂度同样是 \(O(n)\),但空间复杂度从 \(O(n)\) 优化到 \(O(1)\):

#include<bits/stdc++.h>
#define ull unsigned long long 
using namespace std;
ull n,pw=1;
int main(){cin>>n;
	for(int i=1;i<=n;i++)pw*=2;
	cout<<pw-1;
}

空间是优化不了了,再考虑优化时间。我们发现我们推的式子里有 \(2^n\) ,考虑快速幂将时间复杂度优化成 \(O(logn)\)。

前置知识:\(x^k\)中,\(x\)为底数,\(k\) 为指数;\(x^{a+b}=x^a×x^b\),

考虑将 \(n\) 表示成二进制的形式。
举个例子:假设我们要计算 \(2^{11}\),实际转变为求 \(2^{(1011)_2}\)。考虑把这个二进制数拆开成每一位。得到 \(2^{(0001)_2}×2^{(0010)_2}×2^{(1000)_2}\)。再把它们的指数重新表示为十进制数。即 \(2^1×2^2×2^8\)。实际上只需要把底数不断平方就可以算出它们,具体的可以自己上网学习。

所以我们就可以用 \(O(logn)\) 的时间复杂度通过。

#include<bits/stdc++.h>				//此代码浅压 
#define ull unsigned long long 
using namespace std;
ull n;
ull ksm(ull x,ull k){ull sum=1;
	while(k){if(k&1)sum*=x;
		x*=x,k>>=1;
	}
	return sum;
}
int main(){
	cin>>n,cout<<ksm(2,n)-1;
}

对于这道题实际只需 \(O(n)\),如果题目要求结果取模,则 \(n\) 可以开到 \(10^7\);若 \(O(logn)\) ,则 \(n\) 至少可以开到 \(10^{18}\)。若 \(n\) 再开大一些,则考虑用字符串来保存 \(n\),并使用高精除。

总结(引用于《具体数学》):汉诺塔的递推(递归)式是在各种应用中出现的诸多问题的典范,在寻求像 \(T_n\) 这样有意义的量的封闭形式的表达式时,我们经过了如下三个阶段:

(1)研究小的情形,这有助于我们洞察该问题,且对第二第三阶段有所帮助。

(2)对有意义的量求出数学表达式(并证明)。

(3)对数学表达式求出封闭形式(并证明)。

感谢观看,求赞(qwq)。

标签:圆盘,题解,复杂度,long,int,汉诺塔,ull,移动,河内
From: https://www.cnblogs.com/Exotic-sum/p/17753178.html

相关文章

  • CF1142D Foreigner题解
    CF1142DForeigner题解前言:题目含义真的好难理解呜呜。遇到的dp套dp的第三题,所以深入进行了理解。参考博文:https://www.cnblogs.com/AWhiteWall/p/16479483.html题意简化:先定义了不充分。首先数字$[1,9]$都不充分,注意没有$0$。当这个数字(设为$x$)大于等于$10$时......
  • 题解 CF457F 【An easy problem about trees】
    尝试理解,感谢cz_xuyixuan的题解。算作是很多情况的补充说明。我们不妨先二分答案,将\(\gemid\)的设为\(1\),\(<mid\)的设为\(0\),于是问题转化为了权值均为\(0/1\)的版本。我们称一棵树的大小为其非叶节点数。我们称一棵大小为奇数的树为奇树,大小为偶数的树为偶树。对......
  • 题解 - CF1972E - Divisors and Table
    这题正解是虚树,本解法卡常,仅适合不会虚树的。(例如本人)注意:下文中根节点深度定义为1.第一步:转化问题我们把$g(x,y,z)$拆开,考虑每个质数是哪些点的因子。包含这个质数的点构成一个点集,我们只需求这个点集S的$\sum\limits_{x,y,z\inS}f(x,y,z)$。第二步:对......
  • P4801题解
    解题思路:确实是一道很好的贪心,但由于加上了水这个影响因素,使题目复杂度上升了不少。(考虑的东西多了嘛)输个入。对饼干温度无脑排序。求最小值。求最大值(用双指针做,后面会讲)。解题过程:先输入(这个步骤就不用我讲了)inta[1000005];longlongn,ws;longlongmin......
  • Codeforces Round 902 (Div. 2) (CF1877) B、C、D 题解
    B题目大意你要传话给\(n\)个人,每传一下话需要花费\(p\),当一个人被传话后,他可以最多传给\(a_i\)个人,每次花费\(b_i\)。问把话传给\(n\)个人的最小花费。分析首先传给第一个人只少要\(p\)下来贪心,每次让花费最小、且能够传话的人去传话。考虑建一个堆,堆内的信息是......
  • 题解 尼克的任务
    有一种和题解区完全不同的做法。首先将所有任务按照时间从小到大排序,接着用\(f_i\)表示处理前\(i\)个任务所能得到的最大空闲时间。回顾一下需要满足的条件:再某个有任务的时刻,如果尼克是空闲的,就必须从中选择一个任务做。那么我们对于第\(i\)个任务,枚举上一个做的任务\(j......
  • P7710 [Ynoi2077] stdmxeypz 题解
    P7710[Ynoi2077]stdmxeypz题解我的第一道Ynoi题,体验感不高,调了大半天,最后发现有个地方\(B_1\)写成\(B_2\)了。分析树上问题,明显是要转到树下的,所以DFS序是一定要求的。有关树上距离,所以\(deep\)数组也是一定要求的。所以我们现在把问题转化成了:给你三个序列\(......
  • 「Round C10 B」间隔 题解
    简要题意本题有\(T\)组数据。给定一个由\(n\)个元素构成的正整数数列\(a_1,a_2,a_3...a_{n-1},a_n\)。问至少需要插入多少个整数才能使得\(a\)的各相邻元素之差相等(不能插入在头尾)。\(a\)数列保证是单调不减的。\(1\len\le10^6,0\lea_i\le10^6,1\leT\le1......
  • DBeaver [安装/问题解决]
     DBeaverMac版软件简介DBeaverMac版是一款专门为开发人员和数据库管理员设计的免费开源通用数据库工具。软件的易用性是它的宗旨,是经过精心设计和开发的数据库管理工具。免费、跨平台、基于开源框架和允许各种扩展写作(插件)。下载地址......
  • 【题解】CodeForces-1874/1875
    CodeForces-1875AJellyfishandUndertale一定是等待降到\(1\)或者能补满到\(a\)时才使用工具,依题意模拟即可。提交记录:Submission-CodeForcesCodeForces-1874AJellyfishandGame这种题目有点思路但是不是很会。赛时第一发写得根据奇偶性判断,\(k\)为偶数错了,然后感......