蓝桥杯:递归类型题

1.计算年龄
5个人坐在一起,问第5个人多少岁,他说比第4个人大2岁。问第4个人多少岁,他说比第3个人大2岁。问第3个人多少岁,他说比第2个人大2岁。问第2个人多少岁,他说比第1个人大2岁。问第1个人多少岁,他说是10岁。请问第5个人多大?
-------------------------------------------------------------------------------------------------------------
题目分析:
递归用在那些拐弯抹角的地方,用的是堆栈,存在效率问题,所以多用递推别用递归
递归的过程:回溯-结束递归条件-递推结果
(因为递归占内存:所以是慎选的方法)
答案为18
public static int getYear(int num) {
     if(num<=1){
          return 10;
     }else{
          return getYear(num-1)+2;
     }
}

2.组合数
从4个人中选2个人参加活动,一共有6种选法。
从n个人中选m个人参加活动,一共有多少种选法?下面的函数实现了这个功能。
请仔细分析代码,填写缺少的部分(下划线部分)。
int f(int n, int m)
{
if(m>n) return 0;
     if(m==0) _______________;
return f(n-1,m-1) + _____________;
}
--------------------------------------------------------------------------------------------------------------
题目分析:
1.当要选的人数多于总人数的时候,有0种情况
2.当要选的人数为0的时候,只有一种情况,全部给你
3.而递归的真正意义在于把4个人选2个人根据选不选这个人分割成:
     a.当我选这个人,剩下的情况就是n-1个人中选m-1个人。
     b.当我不选这个人的时候,剩下的就是n-1个人中选m个人。
(这个递归就是将一个算式分割成两个残缺的算式,从而利用他们的差别获取答案)

public static int f(int n, int m) {
     if(m>n){
          return 0;
     }
     if(m==0){
          return 1;
     }
     return f(n-1,m-1) + f(n-1,m);
}
程序的结点有两个:1.当取得人数多于总人数的时候     2.当要取的人数为0的时候,只有1种情况
程序的公式采用的是将取人的过程分成两种情况:1.取了指定一个人  2.没有取这个人  两种情况的总和就是取人的所有可能
结果:当输入4个取2个,结果为6


3.Fibonacci数列

Fibonacci数列的递推公式为:Fn=Fn-1+Fn-2,其中F1=F2=1。

当n比较大时,Fn也非常大,现在我们想知道,Fn除以10007的余数是多少。
输入格式
输入包含一个整数n。
输出格式
输出一行,包含一个整数,表示Fn除以10007的余数。

说明:在本题中,答案是要求Fn除以10007的余数,因此我们只要能算出这个余数即可,而不需要先计算出Fn的准确值,再将计算的结果除以10007取余数,直接计算余数往往比先算出原数再取余简单。
样例输入
10
样例输出
55
样例输入
22
样例输出
7704
数据规模与约定
1 <= n <= 1,000,000。
--------------------------------------------------------------------------------------------------------------
题目分析:
1.我选用简单的递归方式来获取结果:
public static int get(int num) {
     if(num==1||num==2){
          return 1;
     }
     return get(num-1)+get(num-2);
}
虽然部分结果出来了,但是运行严重超时,没有用到 直接计算余数往往比先算出原数再取余简 这个知识点。
2.所以抛弃递归,采用递推正推的方式,不是知道1,2脚标的值么,一直加下去。(过程可以mod10007,切掉开头,对于要求的结果不会什么影响)
    ...
     al.add(0);
     al.add(1); //1
     al.add(1);  //2

     for (int i = 1; i <= n; i++) {
          al.add((al.get(i)+al.get(i+1))%10007);   //3开始
     }
     System.out.println(al.get(n));
这里用0补上0脚标的位置,1,2脚标的位置用1补上,然后根据前面的元素做循环将后面的元素补上,最后输出所要求的脚标元素
4.未名湖边的烦恼
问题描述
  每年冬天,北大未名湖上都是滑冰的好地方。北大体育组准备了许多冰鞋,可是人太多了,每天下午收工后,常常一双冰鞋都不剩。
  每天早上,租鞋窗口都会排起长龙,假设有还鞋的m个,有需要租鞋的n个。现在的问题是,这些人有多少种排法,可以避免出现体育组没有冰鞋可租的尴尬场面。(两个同样需求的人(比如都是租鞋或都是还鞋)交换位置是同一种排法)
输入格式
  两个整数,表示m和n
输出格式
  一个整数,表示队伍的排法的方案数。
样例输入
3 2
样例输出
5
数据规模和约定
  m,n∈[0,18]
-------------------------------------------------------------------------------------------------------------
题目分析:
1.m人还鞋,n人借,结点就在于0人借的时候,排序有一种
2. m人还,n人借可以分割:a.当少了一个人还  b.当少了一个人借(递归的情况就是两个式子的总和还是完整的。和选人的区别是,选人是选中+不选中。而鞋子是缺这+缺那)
public static int shoeSort(int m, int n) {
     if(n>m){   //不符合的情况
          return 0;
     }
     if(n==0){     //取得人数为0,一种情况
          return 1;
     }
     return shoeSort(m-1, n)+shoeSort(m, n-1);   //少一个存的+少一个取=刚好平衡
}


5.斐波那契数列
0,1,1,2,3,5,8,13,21,34,55...称为斐波那契数列。
数列的第0项是0,第1项和第2项是1,第10项是55,它的第100项是多少?
--------------------------------------------------------------------------------------------------------------------
题目分析:
1.要在不用递推的情况下用速度比较快的递归,所以要减少递归调用的次数。
...
public static long f(int num,long[] arr) {
     if(arr[num]!=0){
          return arr[num];
     }
     if(num==1||num==2){
          return arr[num]=1;
     }
     return arr[num]=f(num-1,arr)+f(num-2,arr);
}
注意要点:这一题定义一个数组,然后传过来存储已经算出的信息。(其脚标对应没关系,能提取就行)
答案是3736710778780434371


6.搭积木
小明最近喜欢搭数字积木,
一共有10块积木,每个积木上有一个数字,0~9。
搭积木规则:
每个积木放到其它两个积木的上面,并且一定比下面的两个积木数字小。
最后搭成4层的金字塔形,必须用完所有的积木。
下面是两种合格的搭法:

   0
  1 2
 3 4 5
6 7 8 9

   0
  3 1
 7 5 2
9 8 6 4   

请你计算这样的搭法一共有多少种?
请填表示总数目的数字。

题目分析:
1.首先要获取的数量,所以定义一个计数器
2.要存储数字,新建一个数字数组。但是有一些数字用过的就不要再用,所以在定义一个标识数组
3.执行方法,用对数组的一项赋值,因为有很多值要尝试,所以用for循环。为了防止赋值重复,需要进行判断标识,当前这个数有没有用过。
4.没有用的话使用这个数,标识使用过了,然后递归执行获取下一个数。但是当结束这条路要回溯标识。

注意事项:
1.排列的问题在节点处进行判断,将每一项进行比较筛选

 ... if(num==10){
          if(check()){   //判断是否符合情况
              count++;
          }
          return;
     }

     for (int i = 0; i < 10; i++) {
          arr[num]=i;
          if(!isExist[arr[num]]){
              isExist[arr[num]]=true;
              f(num+1);
              isExist[arr[num]]=false;
          }
     }...

答案是: 768(完成时间:8min)
要点注意:当要做多个数的全排列的时候,使用回溯:设置路线的判断,不行就回溯(可以解决重复数字的问题)
7. 第39级台阶
    小明刚刚看完电影《第39级台阶》,离开电影院的时候,他数了数礼堂前的台阶数,恰好是39级!
    站在台阶前,他突然又想着一个问题:
    如果我每一步只能迈上1个或2个台阶。先迈左脚,然后左右交替,最后一步是迈右脚,也就是说一共要走偶数步。那么,上完39级台阶,有多少种不同的上法呢?
    请你利用计算机的优势,帮助小明寻找答案。

题目分析:
1.多种情况使用递归的方式
2.解决减过多的问题,设置结点:当小于0,直接return上一层
3.最后的结点,当阶梯走完,验证偶数步才++,否者还是需要return上一层
4.两种情况交融配合的方式,将一阶和二阶写在一起
...
public static void walk(int sta, int step) {
     if(sta<0){   //走过头
          return;
     }
     if(sta==0){
          if(step%2==0){
              count++;
          }
          return;
     }
     walk(sta-1,step+1);
     walk(sta-2,step+1);
}

注意事项:
1.阶梯结束不代表走的是偶数步,所以需要添加计步器,最后判断步数是不是偶数步进行筛选
答案是: 51167078   (6min26完成)

8.奇怪的比赛
某电视台举办了低碳生活大奖赛。题目的计分规则相当奇怪:
(1)每位选手需要回答10个问题(其编号为1到10),越后面越有难度。答对的,当前分数翻倍;答错了则扣掉与题号相同的分数(选手必须回答问题,不回答按错误处理)。
 (2)每位选手都有一个起步的分数为10分。 某获胜选手最终得分刚好是100分,如果不让你看比赛过程,你能推断出他(她)哪个题目答对了,哪个题目答错了吗?
 (3)如果把答对的记为1,答错的记为0,则10个题目的回答情况可以用仅含有1和0的串来表示。例如:0010110011 就是可能的情况。
 (4) 你的任务是算出所有可能情况。每个答案占一行。

题目分析:
1.这里用递归解,每一层变化的数据有:a.题目数 b.分数 c.对错情况   (注意分数减的是下一题:+1)
2.递归的结点是n==10
     public static void main(String[] args) {
          f(0,10,"");   //从0到10
     }

     public static void f(int num,int fen,String st) {
          if(num==10){      //0执行第1题
              if(fen==100){
                   System.out.println(st);
              }
              return;
          }else{

              f(num+1, fen*2, st+"1");
              f(num+1, fen-num-1, st+"0");   //题号和num错开一位,所以应该减去下一题的题号
          }
     }

题目要点:从第0道题的循环就分数变化,所以第10道题不用分数变化,而是用成数据返回
答案是:
1011010000
0111010000
0010110011
要点注意:其实0解决的是第一题,所以减去题号要减去脚标下一位的题号

9.全排列
题目n个数字进行排列

题目分析:
1.全排列要求的就是把所有可能的情况展现出来
2.结点:当cur指标指到最后一个n的时候,输出数组所有文字
3.递归式,for循环往里面填入一个数字,至于数字是什么,通过判断数字有没有出现过,没有的话填入数据,并且将指针移向下一位继续执行。
     if(cur==num){
          for (int i = 0; i < arr.length; i++) {
              System.out.print(arr[i]);
          }
          System.out.println();
     }else{
          for (int i = 1; i <=num; i++) {
              arr[cur]=i;
              boolean flag=false;
              for (int j = 0; j < cur; j++) {
                   if(arr[j]==i){
                        flag=true;
                   }
              }
              if(!flag){
                   isExist[arr[cur]]=true;
                   f(cur+1);
                   isExist[arr[cur]]=false;
              }
          }
     }
注意事项:排序的数字由1到当前这个数,而cur指针是从0开始,当它等于这个数的时候其实就是超出的位置,做节点判断即可。
... for (int i = 1; i <= A.length; i++) {
        boolean isExist=false;
        for (int j = 0; j < cur; j++) {
            if(A[j]==i){
                isExist=true;
            }
        }
        if(!isExist){
            A[cur]=i;
            prirnt(n, A, cur+1);
        }
    }...
注意事项:cur限制查看存在的枚举范围
这里有两个注意要点:
     a.用普通数组,因为普通数组容易随意替换该位置的数
     b.因为递归的return后继续运行机制,所以判断数字是否重复不能用数组的长度,而是用根据层数变化的cur(和回溯的区别就是,这里将每个数放进去之前进行筛选,其每种情况都需要大量筛选,占内存比较大)
当输入6个数:结果为
1 2 3       1 3 2       2 1 3       2 3 1       3 1 2       3 2 1

10.李白打酒
    话说大诗人李白,一生好饮。幸好他从不开车。
    一天,他提着酒壶,从家里出来,酒壶中有酒2斗。他边走边唱:
    无事街上走,提壶去打酒。
    逢店加一倍,遇花喝一斗。

    这一路上,他一共遇到店5次,遇到花10次,已知最后一次遇到的是花,他正好把酒喝光了。

    请你计算李白遇到店和花的次序,可以把遇店记为a,遇花记为b。则:babaabbabbabbbb 就是合理的次序。像这样的答案一共有多少呢?请你计算出所有可能方案的个数(包含题目给出的)。

题目分析:
1.用递归推出所有情况,进行筛选
public static void f(int d, int h,int all,int type,int j) {    //type店0花1
     if(all>=15){
          if(d==5&&h==10&&type==1&&j==0){
              count++;
          }
          return;
     }else{
          f(d+1, h, all+1, 0, j*2);
          f(d, h+1, all+1, 1, j-1);
     }
}
为了简化参数,其实可以设定第10个是花,所以限制到第九,剩下如果是1就说明成立。注意传进来的酒是2斗!!!
public static void dfs(int d, int h,int j) {    //type店0花1
     if(d>5||h>9){return;}
     if(d==5&&h==9){
          if(j==1){
              count++;
          }
          return;
     }
     dfs(d+1, h, j*2);
     dfs(d, h+1, j-1);
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值