递推问题求解过程:
确定状态可以看有多少个未知数;
递推关系即将 f[i] 用之前推出的值表示;
最后用程序实现就简单了。
例1. 斐波那契数列
简单,不赘述。
f[1] = 0;
f[2] = 1;
f[i] = f[i - 1] + f[i - 2];
同样,走楼梯问题、多米诺骨牌问题公式相同,因为其递推关系相同
- 多米诺骨牌问题
- 走楼梯问题
一个人爬楼梯,每次只能爬1个或两个台阶,假设有n个台阶,那么这个人有多少种不同的爬楼梯方法?
走楼梯问题和多米诺骨牌问题其他解法:
第一列若是一阶或竖格,总共有 1 * f[i - 1] 种算法;
第一列若是二阶或横格,总共有 1 * f[i - 2] 种算法;
故 f[i] = f[i - 1] + f[i - 2];
例2. 汉诺塔问题
如果有 n 个盘子,转换中间时一定有:
第 n 个盘子在 c 柱上,其余盘子在 b 柱上
所以,应先用 f[i - 1] 步将 1 ~ n - 1 的盘子移到柱,用一步将 n 盘移到c柱上,再 f[i - 1] 次将 b 柱上的盘子移到 c 柱上。
所以递推公式是 :
f[1] = 1;
f[i] = f[i - 1] * 2 + 1;
由此可以推导出:
f[i] = qpow(2, i) - 1;
例3. 放球问题
题中有 2 个状态 n、r,所以需要二维数组。
f[i][j] 表示第 i 个球放在 j 个盒子里的方法数;
// i == j
f[i][j] = 1;
//i < j
f[i][j] = 0;
// j == 1
f[i][j] = 1;
第 i 个球,若单独放进一个盒子里,那么前 i - 1个球的、放入了 j - 1 个盒子,方法有 f[i - 1][j - 1] 种;
若第 i 个球和其他球放到一起,那么有f[i - 1][j] 种情况。但是第 i 个球可以放入 j 个盒子中任意一个,所以方法数为 f[i - 1][j] * j
综上,
f[i][j] = f[i - 1][j - 1] + f[i - 1][j] * j;
答案为f[n][r],时间复杂度为 O(n * r)
例4. 错排问题
n个编号为1到n的同学手里都有一本书,n本书互不相同,现在把这n本书收起来,再重新分发给同学,要求每个同学必须都有书,并且每个同学现在拿到的书必须不同于之前手里的那本书,问有多少种分发方案?
f[1] = 0;
f[2] = 1;
f[i] = (i - 1) * (f[i - 1] + f[i - 2]);
例5. 统计二叉树问题
分析如下:
单取一个点作为根节点,其余点中 j 个点作为左节点,剩下的 i - j - 1 个节点就是右节点了。
// i = 0
f[i] = 1;
// i >= 1;
例6. 出栈序列
1 最特殊,用 1 做判断
(1 是最先入栈的,可作为判断依据)
设 1 出现在 j 处,1 ~ j - 1 个数一定对应 2 ~ j。这 2 ~ j 的出栈顺序有 f[j - 1] 种(这 f[j - 1] 种中以 2 为界,但不用考虑,因为在之前算的时候是以 1 为界的)
j + 1 ~ i 上的数也一定是 j + 1 ~ j,情况有 f[i - j]种,理由同上
公式:
f[1] = 1;
// i >= 0;
例7. 出栈序列
n个数,n-1个乘号
类似上题,本次考虑的是乘号使用的先后,不多记录。
例8. 凸多边形划分
状态:f[n] 表示凸 n 边形的拆分方案总数。
由题目中的要求可知一个凸 n 边形的任意一条边都必然是一个三角形的一条边,边 P1Pn 也不例外,再根据“不在同一直线上的三点可以确定一个三角形”,只要在 P2,P3,……,Pn-1 点中找一个点 Pk(1 < k < n),与 P1、Pn共同构成一个三角形的三个顶点,就将n边形分成了三个不相交的部分(如图),我们分别称之为区域 ①、区域 ②、区域 ③,其中区域 ③ 必定是一个三角形,区域 ① 是一个凸 k 边形,区域 ② 是一个凸 n - k + 1 边形,区域 ① 的拆分方案总数是 f[k],区域 ② 的拆分方案数为 f[n - k + 1],故包含 △P1PkPn 的 n 边形的拆分方案数为 f[k] * f[n - k + 1] 种,而 Pk 可以是 P2,P3,……,Pn-1种任一点,根据加法原理,得以下递推关系式:
// f[2] = 1;
// i > 2 时:
作者:Rotch
日期:2020-10-06
修改:2020-10-06
[2020-10-06]:加入例题 8 凸多边形划分