目录
回顾 递推算法1
在上次的递推算法中,我们讲解了6道例题,在最后的一个例题中,我们没有给出标准程序,在本期,我们将给出例题六的标准程序,并讲解五种典型的递推关系,还会留下2道的小练习。
例题六:过河卒
题目描述
见上期
标准程序
#include <iostream>
using namespace std;
long long dp[25][25]={0};
int dx[9]={0,-1,-2,-2,-1, 1, 2, 2, 1};
int dy[9]={0, 2, 1,-1,-2, 2, 1,-1,-2};
int n,m,x,y;
int main()
{
cin>>n>>m>>x>>y;
n+=2; m+=2; x+=2; y+=2;
for (int i=0;i<9;++i)
dp[x+dx[i]][y+dy[i]]=-1;
for (int i=2;i<=n;++i){
if (dp[i][2]==-1) break;
dp[i][2]=1;
}
for (int i=2;i<=m;++i){
if (dp[2][i]==-1) break;
dp[2][i]=1;
}
if (dp[2][2]==-1 || dp[n][m]==-1)
return 0;
for (int i=3;i<=n;++i)
for (int j=3;j<=m;++j)
if (dp[i][j]!=-1){
if (dp[i-1][j]!=-1) dp[i][j]+=dp[i-1][j];
if (dp[i][j-1]!=-1) dp[i][j]+=dp[i][j-1];
}
cout<<dp[n][m]<<endl;
return 0;
}
五种典型的递推关系
1 Fibonacci数列
在所有的递推关系中,Fibonacci数列应该是最为大家所熟悉的。在最基础的程序设计语言Logo语言中,就有很多这类的题目。而在较为复杂的Basic、Pascal、C语言中,Fibonacci数列类的题目因为解法相对容易一些,逐渐退出了竞赛的舞台。可是这不等于说Fibonacci数列没有研究价值,恰恰相反,一些此类的题目还是能给我们一定的启发的。
Fibonacci数列的代表问题是由意大利著名数学家Fibonacci于1202年提出的“兔子繁殖问题”(又称“Fibonacci问题”)。
问题的提出
有雌雄一对兔子,假定过两个月便可繁殖雌雄各一的一对小兔子。问过n个月后共有多少对兔子?
解
设满x个月共有兔子Fx对,其中当月新生的兔子数目为Nx对。第x-1个月留下的兔子数目设为Fx-1对。则:
Fx=Nx+ Fx-1
Nx=Fx-2 (即第x-2个月的所有兔子到第x个月都有繁殖能力了)
∴ Fx=Fx-1+Fx-2 边界条件:F0=0,F1=1
由上面的递推关系可依次得到
F2=F1+F0=1,F3=F2+F1=2,F4=F3+F2=3,F5=F4+F3=5,……。
Fabonacci数列常出现在比较简单的组合计数问题中,例如以前的竞赛中出现的“骨牌覆盖”问题。在优选法中,Fibonacci数列的用处也得到了较好的体现。
2 Hanoi塔问题
问题的提出
Hanoi塔由n个大小不同的圆盘和三根木柱a,b,c组成。开始时,这n个圆盘由大到小依次套在a柱上,如图3-11所示。
要求把a柱上n个圆盘按下述规则移到c柱上:
(1)一次只能移一个圆盘;
(2)圆盘只能在三个柱上存放;
(3)在移动过程中,不允许大盘压小盘。
问将这n个盘子从a柱移动到c柱上,总计需要移动多少个盘次?
解
设hn为n个盘子从a柱移到c柱所需移动的盘次。显然,当n=1时,只需把a 柱上的盘子直接移动到c柱就可以了,故h1=1。当n=2时,先将a柱上面的小盘子移动到b柱上去;然后将大盘子从a柱移到c 柱;最后,将b柱上的小盘子移到c柱上,共记3个盘次,故h2=3。以此类推,当a柱上有n(n>2)个盘子时,总是先借助c柱把上面的n-1个盘子移动到b柱上,然后把a柱最下面的盘子移动到c柱上;再借助a柱把b柱上的n-1个盘子移动到c柱上;总共移动hn-1+1+hn-1个盘次。
∴hn=2hn-1+1 边界条件:h1=1
3 平面分割问题
问题的提出
设有n条封闭曲线画在平面上,而任何两条封闭曲线恰好相交于两点,且任何三条封闭曲线不相交于同一点,问这些封闭曲线把平面分割成的区域个数。
解
设an为n条封闭曲线把平面分割成的区域个数。 由图3-13可以看出:a2-a1=2;a3-a2=4;a4-a3=6。
从这些式子中可以看出an-an-1=2(n-1)。当然,上面的式子只是我们通过观察4幅图后得出的结论,它的正确性尚不能保证。下面不妨让我们来试着证明一下。当平面上已有n-1条曲线将平面分割成an-1个区域后,第n条曲线每与曲线相交一次,就会增加一个区域,因为平面上已有了n-1条封闭曲线,且第n条曲线与已有的每一条闭曲线恰好相交于两点,且不会与任两条曲线交于同一点,故平面上一共增加2(n-1)个区域,加上已有的an-1个区域,一共有an-1+2(n-1)个区域。所以本题的递推关系是an=an-1+2(n-1),边界条件是a0=1。
平面分割问题是竞赛中经常触及到的一类问题,由于其灵活多变,常常感到棘手,下面的【例7】是另一种平面分割问题,有兴趣的读者不妨自己先试着求一下其中的递推关系。
4 Catalan数
Catalan数首先是由Euler在精确计算对凸n边形的不同的对角三角形剖分的个数问题时得到的,它经常出现在组合计数问题中。
问题的提出
在一个凸n边形中,通过不相交于n边形内部的对角线,把n边形拆分成若干三角形,不同的拆分数目用hn表示,hn即为Catalan数。例如五边形有如下五种拆分方案(图3-14),故h5=5。求对于一个任意的凸n边形相应的hn
解
Catalan数是比较复杂的递推关系,尤其在竞赛的时候,选手很难在较短的时间里建立起正确的递推关系。当然,Catalan数类的问题也可以用搜索的方法来完成,但是,搜索的方法与利用递推关系的方法比较起来,不仅效率低,编程复杂度也陡然提高。
5 第二类Stirling数
在五类典型的递推关系中,第二类Stirling是最不为大家所熟悉的。也正因为如此,我们有必要先解释一下什么是第二类Strling数。
定义2
n个有区别的球放到m个相同的盒子中,要求无一空盒,其不同的方案数用S(n,m)表示,称为第二类Stirling数。
下面就让我们根据定义来推导带两个参数的递推关系——第二类Stirling数。
解:设有n个不同的球,分别用b1,b2,……bn表示。从中取出一个球bn,bn的放法有以下两种:
①bn独自占一个盒子;那么剩下的球只能放在m-1个盒子中,方案数为S(n-1,m-1);
②bn与别的球共占一个盒子;那么可以事先将b1,b2,……bn-1这n-1个球放入m个盒子中,然后再将球bn可以放入其中一个盒子中,方案数为mS(n-1,m)。
综合以上两种情况,可以得出第二类Stirling数定理:
定理
S(n,m)=mS(n-1,m)+S(n-1,m-1) (n>1,m>=1)
边界条件可以由定义2推导出:
S(n,0)=0;S(n,1)=1;S(n,n)=1;S(n,k)=0(k>n)。
第二类Stirling数在竞赛中较少出现,但在竞赛中也有一些题目与其类似,甚至更为复杂。读者不妨自己来试着建立其中的递推关系。
小结
通过上面对五种典型的递推关系建立过程的探讨,可知对待递推类的题目,要具体情况具体分析,通过找到某状态与其前面状态的联系,建立相应的递推关系。
问题提出
(1998合肥市竞赛复试第二题)同一平面内的n(n500)条直线,已知有p(p>=2)条直线相交于同一点,则这n条直线最多能将平面分割成多少个不同的区域?
解
这道题目与第一部分中的平面分割问题十分相似,不同之处就在于线条的曲直以及是否存在共点线条。由于共点直线的特殊性,我们决定先考虑p条相交于一点的直线,然后再考虑剩下的n-p条直线。首先可以直接求出p条相交于一点的直线将平面划分成的区域数为2p个,然后在平面上已经有k(k>=p)条直线的基础上,加上一条直线,最多可以与k条直线相交,而每次相交都会增加一个区域,与最后一条直线相交后,由于直线可以无限延伸,还会再增加一个区域。所以fm=fm-1+m (m>p),边界条件在前面已经计算过了,是fp=2p。虽然这题看上去有两个参数,但是在实际做题中会发现,本题还是属于带一个参数的递推关系。
小练习
今天讲的不多,主要讲了递推关系,这在以后的dp动态规划有很大的帮助,那么现在就是两道小练习,有能力的同学可以尝试用dp这种方法来完成题目哦
问题一:汉诺塔
题目描述
经典的汉诺塔问题经常作为一个递归的经典例题存在。可能有人并不知道汉诺塔问题的典故。汉诺塔来源于印度传说的一个故事,上帝创造世界时作了三根金刚石柱 子,在一根柱子上从下往上按大小顺序摞着64片黄金圆盘。上帝命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放 大圆盘,在三根柱子之间一回只能移动一个圆盘。有预言说,这件事完成时宇宙会在一瞬间闪电式毁灭。也有人相信婆罗门至今仍在一刻不停地搬动着圆盘。恩,当 然这个传说并不可信,如今汉诺塔更多的是作为一个玩具存在。Gardon就收到了一个汉诺塔玩具作为生日礼物。
Gardon很想知道在一次游戏中使用了N个盘子时,他需要多少次移动才能把他们都 移到第三个柱子上?
输入
包含多组数据,每个数据一行,是盘子的数目N(1<=N<=64)。
(VC也用unsigned __int64, g++与dev c用到unsigned long long)
输出
对于每组数据,输出一个数,到达目标需要的最少的移动数。
样例输入 复制
1
3
样例输出 复制
1
7
问题二:平面分割问题
题目描述
设有n条封闭曲线画在平面上,而任何两条封闭曲线恰好相交于两点,且任何三条封闭曲线不相交于同一点,问这些封闭曲线把平面分割成的区域个数。
输入
对每一笔测资,输入只有一行:整数n (0<n<1000)
输出
一行:一个整数
样例输入 复制
1
3
10
30
500
样例输出 复制
2
8
92
872
249502
提示
多笔测资