子曰:“君子周而不比,
小人比而不周。”
君子是团结到周围的同学但是不勾结,小人是勾结别人但是不团结。
本文讲述了递归文章中的汉诺塔问题。
我们需要注意如何拆解成为一个更小的问题。
构造汉诺塔的最优策略以及最小步骤。
通过一种错误的归纳证明强化数学归纳法的理解。
使用一种格雷码的状态存储汉诺塔的状态,并且分析移动过程中的性质。
什么是递归,每个问题的解都依赖于同一问题的更小实例的解。
汉诺塔游戏是这样的游戏
我们需要将左边的塔移动到最右边的塔,每次只能移动一个圆盘,且较大的圆盘在移动过程中不能放置在较小的圆盘上面。
和图片上不一样的是,汉诺塔是64个圆盘。
我们的问题是:最好的解法是上面,要完成这项任务移动多少次才是必须且必须的。
解决这样问题的最好办法就是对他稍加推广。汉诺塔有64个,我们认为有n个圆盘。
这样的好处就是,大大简化问题。因为n不固定,我们就可以使用比较小的塔了。
解决下一个问题就是引入适当的记号:命名并且求解。
我们有一种策略是:
1将n-1个圆盘移到另外一个圆盘上。
2将最底下的盘子移到想要移动的盘子上。
3将n-1个盘子移到想要移动的盘子上。
这样肯定是可以,也就是我们得到了。
那么有没有更快的方法?
没有,因为把第n个圆盘移到终点,一定要将其他的n-1个圆盘移到过渡点,如果不这样一定完不成。
将第n个圆盘移到终点,然后将n-1个圆盘从过渡点移到终点。
这个式子有边界值,也有两两之间的关系。
但是递归式知识给出间接、局部的信息。
但是我们想要得到一目了然的解,也就是我们想要知道有一种封闭形式-闭公式,使得我们可以快速计算,并且理解最小移动次数的特征。
T[1]=1
T[2]=3
T[3]=7
T[4]=15
T[5]=31
T[6]=63
T[7]=127
T[8]=255
T[9]=511
T[10]=1023
华罗庚先生说:“设想一下,如果这个飞跃现象不出现,那么人们一辈子就只能学数数了,而且人生有限,数目无穷,就是学了一辈子,也绝不会学尽呢!”
在《具体数学》中生成我们经过了三个阶段。
(1)研究小的情形,这有助于我们洞察该问题,而且对第二和第三阶段有所帮助。
(2)对有意义的量,求出数学表达式,并且给出证明。对汉诺塔,就是递归式,
(3)对数学表达式求出粉笔形式,并且给予证明,对于汉诺塔,就是递归解。
其实我们可以通过高中的知识进行求解,
#include <bits/stdc++.h>
using namespace std;
#define int long long
int n;
int f[100010];
void dfs(int l,int r,int mid,int n)
{
if(n==1)
{
printf("%d->%d\n",l,r);//只执行一步
return ;
}
dfs(l,mid,r,n-1);//将n-1个圆盘移动到过渡点
printf("%d->%d\n",l,r);//将最底下的元素移到终点
dfs(mid,r,l,n-1);//将n-1个圆盘移动到过渡点。
}
signed main()
{
dfs(1,3,2,5);
}
所有的马都是有相同的颜色的吗?
=
如果最左边不能直接移动到最右边。
如果在这种限制的条件下,我们会在3根柱状上都遇到n个圆盘的每一种正确地叠放。
是否存在n个圆盘在3根柱子上,以某种开始叠放或结束叠放,使得按照卢卡斯原来的规则,需要多于2^n次移动?
由此可以看出,原来的放置方法就是最困难的放置方法。
这就是斐波那契数列,但是要加上高精度。
但是这题需要使用高精度。
#include<bits/stdc++.h>
using namespace std;
struct big
{
vector<int>d;
big()
{
}
big(int w)
{
while(w)
{
d.push_back(w%10);
w/=10;
}
}
big(string s)
{
for(int i=s.size()-1;i>=0;i--)
d.push_back(s[i]-'0');
}
int &operator[](int id)
{
return d[id];
}
size_t size()
{
return d.size();
}
void push(int t)
{
d.push_back(t);
}
void pop()
{
d.pop_back();
}
bool operator<(big b)
{
if(size()!=b.size())
{
return size()<b.size();
}
for(int i=size()-1;i>=0;i--)
{
if(d[i]!=b[i])return d[i]<b[i];
}
return false;
}
bool operator>(big b)
{
return b<*this;
}
bool operator<=(big b)
{
return !(*this>b);
}
bool operator>=(big b)
{
return !(*this<b);
}
};
big operator+(big a,big b)
{
if(a.size()<b.size())
return b+a;
big c;
int t=0;
for(int i =0;i<a.size();i++)
{
t+=a[i];
if(i<b.size())t+=b[i];
c.push(t%10);
t/=10;
}
if(t)c.push(t);
return c;
}
big operator-(big &a,big&b)
{
big c;
for(int i=0,t=0;i<a.size();i++)
{
t=a[i]-t;
if(i<b.size())t-=b[i];
c.push((t+10)%10);
if(t<0)t=1;
else t=0;
}
while(c.size()>1&&c.d.back()==0)
{
c.pop();
}
return c;
}
big operator*(big &a,int b)
{
big c;
int t=0;
for(int i=0;i<a.size()||t;i++)
{
if(i<a.size())t+=a[i]*b;
c.push(t%10);
t/=10;
}
while(c.size()>1&&c.d.back()==0)c.d.pop_back();
return c;
}
big operator*(big &a,big &b)
{
int sz=a.size()+b.size()+1;
big res;
res.d.resize(sz);
for(int i=0;i<a.size();i++)
{
for(int j=0;j<b.size();j++)
{
res[i+j]+=a[i]*b[j];
}
}
for(int i=0;i<sz;i++)
{
res[i+1]+=res[i]/10;
res[i]%=10;
}
for(;res.d.back()==0&&res.size()>=2;)
{
res.d.pop_back();
}
return res;
}
pair<big,int>div(big&a,int b)
{
big c;
int r=0;
for(int i=a.size()-1;i>=0;i--)
{
r=r*10+a[i];
c.push(r/b);
r%=b;
}
reverse(c.d.begin(),c.d.end());
while(c.size()>1&&c.d.back()==0)c.pop();
return {c,r};
}
big operator/(big &a,int b)
{
return div(a,b).first;
}
int operator%(big &a,int b)
{
return div(a,b).second;
}
signed main()
{
big now(1),pre(1);
int n;
cin>>n;
for(int i=1;i<=n-1;i++)
{
auto tmp=now;
now=now+pre;
pre=tmp;
}
for(int i=now.size()-1;i>=0;i--)
{
cout<<now[i];
}
}