递归
来源:互联网 发布:dsm请检查网络设置 编辑:程序博客网 时间:2024/06/05 03:23
总结一下递归
之前看到一张图很有意思,这张图可以来解释递归~
之前的学习中,对递归的解释都是,自己直接或者间接调用自己的就叫递归。
递归有两个要点
1.原问题可以缩小成相似的子问题(这样才能调用自己)
2.有出口。(小问题最后还是要被解决的呀,不然不就无限小?)
这张图中我的小鲤鱼就是出口
抱着(抱着(抱着(我的小鲤鱼)的我)的我)的我,一层一层 缩小范围,最后找到出口,终于抱起了…
怎么证明递归的结果是正确的呢
可以用数学归纳法
1.当小问题的时候,是正确的
2.假设当问题范围是n的时候,问题范围是n+1的时候也是正确的
用hanio问题仔细分析分析~
hanio问题
经典汉诺塔问题
有三根柱子A,B,C,要将所有盘子从A柱移动到C柱,问最少的移动步数(题目中的限制条件就不赘述啦)
分析:
原问题是:n个盘子要从A柱移动到C柱;
解决原问题的话,要先将最大的盘子移到C柱。那么要先将最大盘子上的n-1个盘子移到B柱,然后再将最大盘子从A柱移动到C柱。这个时候,B柱上有n-1个盘子,A柱上没有盘子,问题就转移到了要将B柱子上的n-1个盘子转移到C柱上。
简单的说
n个盘子从A柱转移到C柱上转化成
n-1个盘子从A柱转移到B柱上
第n个盘子从A柱转移到C柱上
n-1个盘子从B柱转移到C柱上
原问题的范围减小,出口是当盘子数为1的时候,直接一步从A柱移到C柱上
证明:
当盘子数为1的时候,是正确的
当盘子数为n是正确的,盘子为n+1的时候,要先将n个盘子从A转移到B,然后第n+1的盘子从A转移到C,将n个盘子从B转移到C。
即证。
#include <iostream>#include <cstring>#include <algorithm>#include <cstdio>#include <cmath>#include <vector>#include <map>#include <set>using namespace std;#define ll long long//基础汉诺塔问题,从n个从A塔移动到C塔ll ans=0;void hanio(int n,int from,int temp,int den){ if(n==1){ ans++;return;} hanio(n-1,from,den,temp); ans++; hanio(n-1,temp,from,den);}int main(){ int n; while(scanf("%d",&n)!=EOF){ ans=0; hanio(n,1,2,3); cout<<ans<<endl; } return 0;}
这里可以根据数据发现一个结论
最小步骤数为
证明,当盘子数为n的时候
迭代可得
新hanio问题 UVA - 10795
题意:也是三根柱子A,B,C。给你开始的状态,第i大的盘子在第j个柱子上,和目标状态,第i大的盘子在第j个柱子上。问从开始状态转移到目标状态的最小操作数。
分析:和上面的hanio问题比较,就是开始的状态和目标状态变了。思想也差不多。
先找到需要移动的最大的盘子k。start[k]表示初始k盘子 在哪根柱子上,ed[k]:表示目标k盘子在哪根柱子上。则我们需要借助柱子temp=6-start[k]-ed[k];(因为柱子的编号是1,2,3)。f(n,from,to):表示n个盘子要从from柱子转移到to柱子上需要的步数。
所以原问题就是f(k-1,start[k],6-start[k]-ed[k])+1+f(k-1,ed[k],6-start[k]-ed[k]),因为步骤是可逆的,从开始状态到目标状态=从开始状态到k状态+目标状态到k状态+1(移动k)
怎么算f(n,n的柱子,to)?当from==to时f(n)=f(n-1,n-1的柱子,to)
否则f(n,n的柱子,to)=f(n-1,n-1的柱子,temp)+1+f(n-1,temp,n-1的目标柱子)
根据基础hanio 问题:f(n-1,temp,n-1的目标柱子)=
using namespace std;#define ll long longconst int maxn = 65;int start[maxn],ed[maxn];ll f(int n,int *p,int to){ if(n<1) return n;//这里不能写成n<=1,因为等于1的时候,有可能为0 if(p[n]==to) return f(n-1,p,to); else return f(n-1,p,6-p[n]-to)+(1ll<<(n-1));}int main(){ int kase=1,n; while(scanf("%d",&n)!=EOF&&n) { for(int i=1;i<=n;i++) scanf("%d",&start[i]); for(int i=1;i<=n;i++) scanf("%d",&ed[i]); int k=n; while(k>=1&&start[k]==ed[k]) k--; ll ans=0; if(k>=1) { int temp=6-start[k]-ed[k]; ans=f(k-1,start,temp)+1+f(k-1,ed,temp); } printf("Case %d: %lld\n",kase++,ans); } return 0;}