算法整理三——递推

算法整理三——递推本文详细介绍了递推算法的基本概念 特点和应用场景 包括菲波那契数列 汉诺塔问题 猴子吃桃等经典问题的递推解法 并提供了相应的 C 代码实现

大家好,欢迎来到IT知识分享网。

目录

递推算法概述

一、求菲波那契数列

二、汉诺塔(Tower of Hanoi)问题

三、猴子吃桃

四、数字三角形(顺推法)

五、骨牌铺满方格

六、吃糖果

七、蜜蜂路线

八、昆虫繁殖【这题有点点难哦】

九、位数问题

十、分苹果

十一、踩方格

十二、爬楼梯(openjudge题目)


递推算法概述

  • 递推/递归既可以理解成是一种算法思想,也可以理解成是一种算法的实现方式
  • 分治算法策略主要通过递归实现,大规模问题分解成小规模问题;动态规划算法主要通过递推实现
  • 递归和递推的核心其实都是递推式
  • 递归:从上到下 从未知到已知解决问题
  • 递推:从下到上 从已知到未知解决问题

(1)算法特点:

  • 一个问题的求解需一系列的计算
  • 在已知条件和所求问题之间总存在着某种相互联系的关系
  • 找到前后过程之间的数量关系(即递推式),分为顺推和逆推。
  • 无论顺推还是逆推,其关键是要找到递推式

(2)关键:

用递推算法解决问题时,关键是找到递推式以及边界条件(临界条件)

(3)顺推、逆推

(4)使用递推方法解决:菲波那契数列、汉诺塔移动次数、猴子吃桃、数字三角形(顺推法)、骨牌铺满方格、蜜蜂路线、吃糖果、昆虫繁殖、位数问题、分苹果、踩方格、爬楼梯(openjudge题目)

一、求菲波那契数列

(Fibonacci)数列的前40个数。这个数列有如下特点:第1、2两个数为1、1。从第3个数开始,该数是其前面两个数之和。即:

算法整理三——递推

 

#include<iostream> using namespace std; int main() {     int f[101],n;     cin>>n;     f[1]=1;f[2]=1;     for(int i=3;i<=n;i++)         f[i]=f[i-1]+f[i-2];     for(int i=0;i<n;i++)     {         if(i%5==0)//5个数一行             cout<<endl;         cout<<f[i]<<'\t';     }     cout<<endl;     return 0; }

二、汉诺塔(Tower of Hanoi)问题

移动规则:

每次只能移动一个圆盘;圆盘可以插在X、 Y和Z中的任何一个塔座上;任何时刻都不能将一个较大的圆盘压在较小的圆盘之上

假设移动n个盘子从A到C需要移动的次数为f(n)

f(0)=0

f(1)=1  临界条件

f(n)=f(n-1)+1+f(n-1)=2*f(n-1)+1 递推式【把n-1从A->B ,把第n个从A->C,把n-1从B->C】

#include<iostream> using namespace std; int main() {     int f[101],n;     cin>>n;     f[0]=0;f[1]=1;     for(int i=2;i<=n;i++)         f[i]=2*f[i-1]+1 ;     cout<<f[n]<<endl;     return 0; }

三、猴子吃桃

猴子第一天采摘了一些桃子,第二天吃了第一天的一半多一个,第三天吃了第二天的一半多一个…直到第十天就剩下一个。问:猴子第一天摘了多少桃子?

f[n]代表第n天剩余的桃子的个数

f[n]=f[n-1]*1/2-1 -> f[n-1]=(f[n]+1)*2  递推式 、逆推

f[10]=1  临界条件

#include<iostream> using namespace std; int main() {     int f[101];     f[10]=1;     for(int i=9;i>=1;i--)         f[i]=(f[i+1]+1)*2;     cout<<f[1]<<endl;     return 0; }

四、数字三角形(顺推法)

请编一个程序计算从顶到底的某处的一条路径,使该路径所经过的数字总和最大。只要求输出总和。

  • 一步可沿左斜线向下或右斜线向下走;
  • 三角形行数小于等于100;
  • 三角形中的数字为0,1,…,99;

算法整理三——递推

 

二维数组存储,格式如右图

f[x][y]表示从(1,1)到(x,y)的路径的最大权值和

f[1][1]=a[1][1] 临界条件

f[x][y]=max{f[x-1][y]f[x-1][y-1]}  递推式

输入:

5

7

3 8

8 1 0

2 7 4 4

4 5 2 6 5

#include <iostream> using namespace std; int a[101][101]={0}; //注意此时二维数组的初值为0 int f[101][101],n; int max(int a,int b){ if(a>b)         return a; return b; } int main()  { cin >> n; for(int i = 1;i <= n;i ++)     for(int j = 1;j <= i;j ++)         cin >> a[i][j]; f[1][1] = a[1][1]; //求出f[2][1],f[2][2]......f[5][5] for(int i = 2;i <= n;i ++)     for(int j = 1;j <= i;j ++)         f[i][j]=max(f[i-1][j-1],f[i-1][j])+a[i][j];    int ans =0; //这个循环求f[5][1]......f[5][5]的大小 for(int i = 1;i <= n;i ++)     ans = max(ans,f[n][i]);    cout << ans << endl; return 0;   }

五、骨牌铺满方格

有 2*n 的一个长方形方格,用一个1*2 的骨牌铺满方格。

编写一个程序,试对给出的任意一个n(n>0), 输出铺法总数。

算法整理三——递推

 

算法分析:

(1)当n=1时,

只能是一种铺法,铺法总数有示为x1=1。

(2)当n=2时,

骨牌可以两个并列竖排,也可以并列横排,再无其他方法,如下左图所示,因此,铺法总数表示为x2=2;

算法整理三——递推

 

  1. 当n=3时,如上图
  2. 当n=4时,如下图

算法整理三——递推

 

算法分析:

推出一般规律:对一般的n,xn可以这样来考虑

若第一个骨牌是竖排列放置

     剩下有n-1个骨牌需要排列,这时排列方法数为xn-1

若第一个骨牌是横排列放置

     整个方格至少有2个骨牌是横排列(1*2骨牌),因此剩下n-2个骨牌需要排列,这是骨牌排列方法数为xn-2

假设f(n)表示n列的铺发总数

F(1)=1

F(2)=2

F(n)=f(n-1)+f(n-2)

#include <iostream> using namespace std; int main(){     int n;//代表有几列     cin>>n; int f[101]; f[1]=1;f[2]=2; for(int i=3;i<=n;i++)         f[i]=f[i-1]+f[i-2];     cout<<f[n]<<endl; }

六、吃糖果

名名的妈妈从外地出差回来,带了一盒好吃又精美的巧克力给名名(盒内共有 N 块巧克力,20 > N >0)。

妈妈告诉名名每天可以吃一块或者两块巧克力。假设名名每天都吃巧克力,问名名共有多少种不同的吃完巧克力的方案。

分析:

每天只有两个选择,吃1块或者吃2块

f[n]: 表示吃完 n 块巧克力的方案数

n=1,1种方案  f[1]=1

n=2,2种方案  则名名可以第1天吃1块,第2天吃1块,也可以第1天吃2块, f[2]=2

…….

如果n= i,第一天要么吃掉1块,要么吃掉2块,所以剩余 i-1 块或者 i-2 块,因此,

     f[i] = f[i – 1] + f[i – 2]         递推式

#include <iostream> using namespace std; int main(){     int n;//代表有几列     cin>>n; int f[101]; f[1]=1;f[2]=2; for(int i=3;i<=n;i++)         f[i]=f[i-1]+f[i-2];     cout<<f[n]<<endl; } 【代码实际与上一题骨牌铺满方格完全一致】

七、蜜蜂路线

一只蜜蜂在下图所示的数字蜂房上爬动,已知它只能从标号小的蜂房爬到标号大的相邻蜂房,现在问:蜜蜂从蜂房m开始爬到蜂房n,m<n,有多少种爬行路线?

算法整理三——递推

 

设f[i]表示蜜蜂爬到i位置的爬行路线数

例如蜜蜂爬到4只有可能从3来,或者从2来

故f[4]=f[3]+f[2]

即f[i]=f[i-1]+f[i-2] 递推式

f[m]=1,f[m+1]=1   临界值

【代码都是大同小异】

#include <iostream> using namespace std; int main(){     int m,n;//代表有几列     cin>>m>>n; int f[101]; f[m]=1;f[m+1]=1; for(int i=m+2;i<=n;i++)         f[i]=f[i-1]+f[i-2];     cout<<f[n]<<endl; }

八、昆虫繁殖【这题有点点难哦】

科学家在热带森林中发现了一种特殊的昆虫,这种昆虫的繁殖能力很强。

每对成虫过x个月产y对卵,每对卵要过两个月长成成虫

假设每个成虫不死,第一个月只有一对成虫,且卵长成成虫后的第一个月内不产卵(过X个月产卵).

问过Z个月以后,共有成虫多少对?1=<x<=20,1<=y<=20,x=<z<=50

本月成虫的数量=前2个月卵的数量+前1个月成虫的数量

本月卵的数量=前x月成虫的数量*y

设f[i]表示第 i 月昆虫成虫的数量,b[i]表示第 i 月的新虫卵的数目

b[i] = f[ i – x] * y; (成虫经过x月产卵 y对)
f[i] = f[ i – 1] + b[ i – 2]; (卵经过2个月长成成虫)

F[1]=1  b[1]=0

#include <iostream>

using namespace std;

int main(){

    int x,y,z;

    cin>>x>>y>>z;

int f[101],b[101];

//在x个月之内成虫都是,都不产卵,所以赋初值一定是用循环

    for(int i=1;i<=x;i++){

       f[i]=1;

       b[i]=0;

  }

for(int i=x+1;i<=z+1;i++)

        //因为要统计到第z个月后,所以要for到z+1

    {

        b[i]=y*f[i-x];

        f[i]=f[i-1]+b[i-2];

    }

    cout<<f[z+1]<<endl;

    return 0;

}

九、位数问题

在所有的n位数中,有多少个数中有偶数个数字3 ? (说明,0是偶数

f[i][0]表示前i位中有偶数个3的数目

   前i-1位有偶数个3,第i位不能为3

   前i-1为有奇数的3,第i位是3

f[i][0]=f[i-1][0]*9+f[i-1][1]【乘以9是因为,第i位除了3以外的每一个数字】

f[i][1]表示前i位中有奇数个3的数目

同理,f[i][1]=f[i-1][1]*9+f[i-1][0]

f[1][1]=1;f[1][0]=9;//1位数中有偶数个3的有9个,有奇数个3的1个

#include <iostream> using namespace std; int main(){     int n,x=9;//n位数     cin>>n;     int f[101][2];     f[1][1]=1;f[1][0]=9;//f[1][0]表示有偶数个3的数目     for(int i=2;i<=n;i++)     {         if(i==n)//i是最高位             x--;//最高位不能是0         f[i][0]=f[i-1][0]*x+f[i-1][1];         f[i][1]=f[i-1][1]*x+f[i-1][0];     }     cout<<f[n][0]<<endl;     return 0; }

十、分苹果

把 m 个同样的苹果放在 n 个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?

说明:5,1,1和1,5,1 是同一种分法。

【输入格式】一行,包含二个整数 m 和 n,以空格分开。1<=m,n<=10。

【输出格式】用一行输出相应的K。

设f(m,n) 为m个苹果,n个盘子的放法数目

  1. n>m:至少有n-m个盘子永远空着 即f(m,n) = f(m,m)
  2. 当n<=m:

有盘子空着,至少一个盘子空着  f(m,n) = f(m,n-1)  

所有盘子都有苹果,多出m-n个苹果,拿走不影响放法的个数  f(m,n)=f(m-n,n)

故当n<=m时,f(m,n)=f(m,n-1)+f(m-n,n)

当没有苹果可放时,即m=0时,定义为1种放法;

边界条件: f[m,1]=1  f[i,0]=0   f[0][n]=1

#include <iostream> using namespace std; int main(){     int m,n,f[11][11];//第一维表示苹果个数,第二维表示盘子个数     cin>>m>>n;     //初始化如果盘子n的个数是0或1时初始化     for(int i=0;i<=m;i++)     {         f[i][0]=0;         f[i][1]=1;     }     //初始化如果苹果个数为0     for(int i=0;i<=n;i++)         f[0][i]=1;     for(int i=1;i<=m;i++)//i是苹果个数     {         for(int j=1;j<=n;j++)//j是盘子个数         {             if(i<j)                 f[i][j]=f[i][i];             else                 f[i][j]=f[i][j-1]+f[i-j][j];         }     }     cout<<f[m][n]<<endl; }

十一、踩方格

有一个方格矩阵,矩阵边界在无穷远处。我们做如下假设:

a、只能向北、东、西三个方向走;

b、每走一步时,只能从当前方格移动一格,走到某个相邻的方格上;

c、走过的格子立即塌陷无法再走第二次;

请问:如果允许在方格矩阵上走n步,共有多少种不同的方案。2种走法只要有一步不一样,即被认为是不同的方案

 分析:

走到第i步的时候可能是从左边来的,也可能从右边来的,也可能从上面来的

用up[i], l[i]和 r[i]分别代表第 i 步是向上走,向左走和向右走的不同方案数

我们要求的结果便是:up[i]+ l[i]+r[i]

递推关系:

up[i]=up[i-1]+l[i-1]+r[i-1]

l[i]=up[i-1]+l[i-1]

r[i]=up[i-1]+r[i-1]

初始值

     up[1]=1    l[1]=1    r[1]=1 

十二、爬楼梯(openjudge题目)

小明爬楼梯,他可以每次走1级或者2级,输入楼梯的级数,求不同的走法数。【和吃糖果一样】

算法整理三——递推

 

大家一起加油 !!!

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/123194.html

(0)
上一篇 2024-11-19 18:26
下一篇 2024-11-19 18:33

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信