递归

总结一下递归
之前看到一张图很有意思,这张图可以来解释递归~
这里写图片描述

之前的学习中,对递归的解释都是,自己直接或者间接调用自己的就叫递归。
递归有两个要点
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 long
const 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值