递归

来源:互联网 发布: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;}

这里可以根据数据发现一个结论
最小步骤数为 2n1
证明,当盘子数为n的时候
ans=2h(n1)+1
迭代可得
ans=2n1h(1)+2n2+2n3+...+2+1=2n1

新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的目标柱子)=2n11

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;}
原创粉丝点击