递推算法通过已知的初始条件和递推关系,逐步推导出后续结果。与递归不同,递推通常使用循环结构实现,避免了函数调用的开销,效率更高。
本文将用C++语言,通过几个经典例题,详细讲解递推算法的思想和实现。
一、递推算法基本思想
递推算法的核心是递推关系式和初始条件。
递推关系式描述了当前状态如何由前一个或多个状态推导而来,而初始条件则是递推的起点。
在C++中实现递推,通常遵循以下步骤:
- 定义状态数组:使用数组存储中间结果
- 设置初始条件:根据问题初始化数组的前几项
- 建立递推关系:通过循环按照递推公式计算后续项
- 输出结果:返回或输出目标位置的值
递推与递归的主要区别在于:递推是自底向上的迭代过程,而递归是自顶向下的函数调用过程。递推通常更高效,适合处理线性结构问题。
二、一维递推问题
1. 斐波那契数列
问题描述:斐波那契数列的第1项为1,第2项为1,从第3项开始,每一项都等于前两项之和。
递推关系:f[i] = f[i-1] + f[i-2]初始条件:f[1] = 1, f[2] = 1
C++代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
代码解析:
- 数组
f存储已计算的结果,避免重复计算 - 循环从3开始,依次计算每一项
- 当n≤45时,结果在int范围内(约21亿内)
2. 爬楼梯问题
问题描述:有n阶楼梯,每次可以爬1阶或2阶,问有多少种不同的爬法。
递推分析:设a[i]表示爬到第i阶楼梯的方法数。由于每次只能爬1阶或2阶,所以到达第i阶只能从第i-1阶爬1阶,或从第i-2阶爬2阶。
递推关系:a[i] = a[i-1] + a[i-2]初始条件:a[1] = 1(爬1阶只有1种方法),a[2] = 2(爬2阶有2种方法)
C++代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
代码解析:
- 这个问题实质上是斐波那契数列的变体,只是初始条件不同
三、二维递推问题
1. 无障碍网格路径计数
问题描述:在一个m×n的网格中,从左上角(1,1)出发,每次只能向右或向下移动一步,要到达右下角(m,n),问有多少条不同的路径。
递推分析:设b[i][j]表示从起点到达坐标(i,j)的路径数。由于只能向右或向下移动,要到达(i,j),只能从上方(i-1,j)或左方(i,j-1)过来。
递推关系:b[i][j] = b[i-1][j] + b[i][j-1]边界条件:第一行和第一列的所有位置都只有1条路径
C++代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
代码解析:
- 数组
b[i][j]表示到达(i,j)的路径数 - 初始化第一行和第一列为1,因为沿着边线只有一条路径
- 双重循环从(2,2)开始递推计算
2. 有障碍网格路径计数
路径计数2(洛谷P1176)
问题描述:一个 N×N 的网格,你一开始在 (1,1),即左上角。每次只能移动到下方相邻的格子或者右方相邻的格子,问到达 (N,N),即右下角有多少种方法。
但是这个问题太简单了,所以现在有 M 个格子上有障碍,即不能走到这 M 个格子上。
递推分析:递推关系与无障碍情况类似,但需要额外考虑障碍物:
- 如果(i,j)是障碍物,则
b[i][j] = true - 否则,
a[i][j] =a[i-1][j] + a[i][j-1]
C++代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
|
四、马走日(过河卒)问题
问题描述:棋盘上有一个卒需要从A点(1,1)走到B点(n,m),卒只能向右或向下移动。棋盘上有一个马,马走"日"字,马所在位置及其控制点(马能走到的8个位置)卒不能通过。
递推分析:这是网格路径计数问题的变体,增加了障碍点(马的控制点)。设b[i][j]表示卒从起点到达(i,j)的路径数,stop[i][j]表示(i,j)是否为障碍点。
递推关系与有障碍网格类似:
- 如果(i,j)是障碍点,则
b[i][j] = 0 - 否则,
b[i][j] = b[i-1][j] + b[i][j-1]
C++代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
|
五、递推算法的核心要点
1. 确定递推状态
递推状态是问题的关键,通常用一个或多个变量表示问题的某个状态。例如:
- 爬楼梯问题:
a[i]表示到达第i阶的方法数 - 网格路径问题:
b[i][j]表示到达(i,j)的路径数
状态的定义需要能够完整描述问题的当前情况,并且能够通过递推关系转移到其他状态。
2. 建立递推关系
递推关系描述了状态之间的转移方式,通常基于问题的限制条件。
例如:
- 爬楼梯:一次只能爬1或2阶 →
a[i] = a[i-1] + a[i-2] - 网格路径:只能向右或向下 →
b[i][j] = b[i-1][j] + b[i][j-1]
3. 设置初始条件
初始条件是递推的起点,必须明确给出。例如:
- 爬楼梯:
a[1] = 1, a[2] = 2 - 网格路径:第一行和第一列都为1(无障碍时)