递归和递推

递归的概念

一个函数、过程、概念或数据结构,如果在其定义或说明内部直接或间接地出现有其本身的引用,或者是为了描述问题的某一状态,必须用到它的上一状态,而描述上一状态,又必须用到它的上一状态……这种用自己来定义的方法,称之为递归或者递归定义。

在程序设计中,过程或函数直接或者间接调用自己,就称为递归调用

递归过程实际上借助于一个递归工作栈来实现的。

首先问题向一个方向一步一步分解,既问题规模逐渐降低,这样,问题向一极推进的过程称之为递归过程;

然后这些问题又按后提出先解决的顺序依次得到解决,最后得到原问题的解。这样,在子问题得到逐一解决的基础上,最后回到原问题的解的变化过程,称之为回归过程。

递归定义的要素

1、递归边界或终止条件。

2、递归定义,使问题向边界条件转化的规则。

var

n:integer;

function fibonacci(x:integer):integer;

begin

if (x=0) or (x=1) then exit(1);

exit(fibonacci(x-1)+fibonacci(x-2));

end;

begin

readln(n);

writeln(fibonacci(n));

end.

集合的划分

设s是一个具有n个元素的集合,s={a1,a2,……,an},现将s划分成K个满足下列条件的子集合s1,s2,……,sk ,且满足:

1.si≠φ

2.si∩sj=φ         (1≤i,j≤k  i≠j)

3.s1∪s2∪s3∪…∪sk=s

则称s1,s2,……,sk是集合s的一个划分。它相当于把s集合中的n个元素a1 ,a2,……,an 放入k个(0<k≤n<30)无标号的盒子中,使得没有一个盒子为空。请你确定n个元素a1 ,a2 ,……,an 放入k个无标号盒子中去的划分数s(n,k)。

先举个例子,设S={1,2,3,4},k=3,不难得出s有6种不同的划分方案,即划分数s(4,3)=6,具体方案为:

{1,2}∪{3}∪{4}

{1,3}∪{2}∪{4}

{1,4}∪{2}∪{3}

{2,3}∪{1}∪{4}

{2,4}∪{1}∪{3}

{3,4}∪{1}∪{2}

考虑一般情况,对于任意的含有n个元素a1 ,a2,……,an的集合s,放入k个无标号的盒子中去,划分数为s(n,k),我们很难凭直觉和经验计算划分数和枚举划分的所有方案,必须归纳出问题的本质。

下面考虑对任一个元素an,则必然出现以下两种情况:

1、{an}是k个子集中的一个,于是我们只要把a1,a2,……,an-1 划分为k-1子集,便解决了本题,这种情况下的划分数共有s(n-1,k-1)个;

2、{an}不是k个子集中的一个,则an必与其它的元素构成一个子集。则问题相当于先把a1,a2,……,an-1 划分成k个子集,这种情况下划分数共有s(n-1,k)个;然后再把元素an加入到k个子集中的任一个中去,共有k种加入方式,这样对于an的每一种加入方式,都可以使集合划分为k个子集,因此根据乘法原理,划分数共有k*s(n-1,k)个。

综合上述两种情况,应用加法原理,得出n个元素的集合{a1,a2,……,an}划分为k个子集的划分数为以下递归公式:

s(n,k)=s(n-1,k-1)+k*s(n-1,k) (n>k,k>0)。

确定s(n,k)的边界条件

首先不能把n个元素不放进任何一个集合中去,即k=0时,s(n,k)=0;

也不可能在不允许空盒的情况下把n个元素放进多于n的k个集合中去,即k>n时,s(n,k)=0;

再者,把n个元素放进一个集合或把n个元素放进n个集合,方案数显然都是1,即k=1或k=n时,s(n,k)=1。

可以得出划分数s(n,k)的递归关系式为:

s(n,k)=s(n-1,k-1)+k*s(n-1,k)            (n>k,k>0)

s(n,k)=0                                     (n<k)或(k=0)

s(n,k)=1                                     (k=1)或(k=n)

递归与非递归的转化

在递归调用的过程中,由于要保留每次调用时的参数和局部变量,而且要经过逐层调用和逐层返回两大过程,程序对于时间和存储空间的耗费可以说是巨大的。因此,在一个问题对于时空要求较高的情况下,有时不得不将递归方法转化成非递归方法,这一过程,常常称之为消除递归。消除递归的过程,其本质上就是要求模拟复杂的递归工作栈的变化过程,虽然递归与非递归在形式上大多能够相互转化,但是,非递归的编程难度要远远高于递归的方法

递推

在问题类型中,每个数据项都和它前面的若干个数据项(或后面的若干个数据项)有一定的关联,这种关联一般是通过一个递推关系式来表示的。求解问题时就从初始的一个或若干个数据项出发,通过递推关系式逐步推进,从而得到最终结果。这种求解法就叫递推法

var

a:array[0..100] of longint;

i:integer;

begin

readln(n);

a[0]:=1;

a[1]:=1;

for i:=2 to n do

a[i]:=a[i-1]+a[i-2];

writeln(a[n]);

end.

算法框架

1、确定求解初始条件

2、求出递推公式

3、从边界出发进行顺推或倒推

4、输出结果

具体详见:

http://download.csdn.net/download/boyxiejunboy/8913331

http://download.csdn.net/download/boyxiejunboy/8913339

http://download.csdn.net/download/boyxiejunboy/8913343

(不用积分下载哦)

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-06 16:30:21

递归和递推的相关文章

【递归与递推】计数器

问题 F: [递归与递推]计数器 题目描述 一本书的页数为N,页码从1开始编起,请你求出全部页码中,用了多少个0,1,2,…,9.其中—个页码不含多余的0,如N=1234时第5页不是0005,只是5. 输入 一个正整数N(N≤109),表示总的页码. 输出 共十行:第k行为数字k-1的个数. 样例输入 11 样例输出 1 4 1 1 1 1 1 1 1 1 #include <iostream> #include <cstring> #include <cstdio>

【递归与递推】电话号码

问题 L: [递归与递推]电话号码 题目描述 电话机上每一个数字下面都写了若干个英文字母.分布如下:       1-abc       2-def       3-ghi       4-ikl       5-mn       6-opq       7-rst       8-uvw       9-xyz  现在给定一个单词表和一串数字密码,请你用单词表中的单词翻译这个密码. 输入 第一行为一个正整数N表示单词表中单词的个数(N≤100): 第二行为一个长度不超过100的数字串,表示密码

递归,递推,记忆化搜索,空间优化(数字三角形)

题目链接:http://poj.org/problem?id=1163 1.递归思想:第一层到最底层的最优路径可以分解为:第一层到第二层来,再加上第二层的最优路径 状态: Time Limit Exceeded #include <algorithm> #include <stdio.h> #define MAX 101 using namespace std; int maps[MAX][MAX]; int n; int Sum(int i,int j) { if(i==n) r

【递归与递推】诸侯安置

[递归与递推]诸侯安置 题目描述 很久以前,有一个强大的帝国,它的国土成正方形状,如图2—2所示. 这个国家有若干诸侯.由于这些诸侯都曾立下赫赫战功,国王准备给他们每人一块封地(正方形中的一格).但是,这些诸侯又非常好战,当两个诸侯位于同一行或同一列时,他们就会开战.如下图2—3为n=3时的国土,阴影部分表示诸侯所处的位置.前两幅图中的诸侯可以互相攻击,第三幅则不可以. 国王自然不愿意看到他的诸侯们互相开战,致使国家动荡不安.  因此,他希望通过合理的安排诸侯所处的位置,使他们两两之间都不能攻击

【递归与递推】排序集合

[递归与递推]排序集合 题目描述 对于集合N={1,2,…,n}的子集,定义一个称之为“小于”的关系:设S1={X1,X2,…,Xi},(X1<X2<…<Xi),S2={Y1, Y2, …,Yj},(Y1<Y2<…<Yj),如果存在一个k,(0≤k≤min(i,j)),使得X1=Y1,…,Xk=Yk,且k=i或X(k+1)<Y(k+1),则称S1“小于”S2.你的任务是,对于任意的n(n≤31)及k(k<2n),求出第k小的子集. 输入 仅一行,包含两个用空

【递归与递推】编码

[递归与递推]编码 题目描述 编码工作常被运用于密文或压缩传输.这里我们用一种最简单的编码方式进行编码:把一些有规律的单词编成数宇. 字母表中共有26个字母{a,b,…,z},这些特殊的单词长度不超过6且字母按升序排列.把所有这样的单词放在一起,按字典顺序排列,一个单词的编码就对应着它在字典中的位置. 例如:    a→1    b→2    z→26    ab→27    ac→28 你的任务就是对于所给的单词,求出它的编码. 输入 仅一行,被编码的单词. 输出 仅一行,对应的编码.如果单词

【递归与递推】遍历问题

[递归与递推]遍历问题 题目描述 我们都很熟悉二叉树的前序.中序.后序遍历,在数据结构中常提出这样的问题:已知一棵二叉树的前序和中序遍历,求它的后序遍历,相应的,已知一棵二叉树的后序遍历和中序遍历序列你也能求出它的前序遍历.然而给定一棵二叉树的前序和后序遍历,你却不能确定其中序遍历序列,考虑如下图中的几棵二叉树: a                      a                         a                        a /               

数字三角形——递归、递推、记忆化搜索

数字三角形 描述: 有一个由非负整数组成的三角形,第一行只有一个数,除了最下行之外没个数的左下方和右下方各有一个数. 问题: 从第一行的数开始,每次可以往左下或右下走一格,直到走到最下行,把沿途经过的数全部加起来.如何走才能使得这个和尽量大? 分析: 不难看出此题是一个动态的决策问题:每次有两种选择--左下或右下.如果用回溯法求出所有的可能的路线,就可以从中选出最优的路线.但和往常一样,回溯法的效率太低:一个n层数字三角形的完整路线有2^n条,当n很大时回溯法的速度将让人无法忍受.因此本题讨论用

【递归与递推】集合的划分

[递归与递推]集合的划分 题目描述 设s是一个具有n个元素的集合,s={a1,a2,……,an},现将s划分成k个满足下列条件的子集合s1,s2,……,sk,且满足:(1)si≠∅(2)si∩sj=∅(3)s1∪s2∪s3∪...∪sk=s则称s1,s2,...,sk是集合s的一个划分.它相当于把s集合中的n个元素a1,a2,...,an放入k个(0<k≤n<30)无标号的盒子中,使得没有一个盒子为空.请你确定n个元素a1,a2,...,an放入k个无标号盒子中去的划分数s(n,k). 输入

nyist 17 -----纯递归纯递推--超时

#include <iostream>#include<stdio.h>#include<string.h>using namespace std;char a[10002];int b[10002];int n,ans;int f(int x){ int i,t; for(i=0;i<=x-1;i++) { t=f(i); if(a[i]<a[x] && b[x]<t+1) b[x]=t+1; } return b[x];} int