一、汉诺塔问题(THE TOWEL OF HANOI)
hanoi塔问题是法国数学家卢卡斯发明的问题,简单的说就是给你三根柱子,其中初始阶段所有的
n
n
n盘子都按照从大到小放置到一条柱子上。如果你觉得表述不清楚,那么请百度这个问题。,现在的问题是:如何移动盘子可以使用最少的步骤把所有的盘子移动到另一个柱子上?在任意时刻,大盘子都在小盘子的下面。
1、对于这种复杂的问题,先研究小的情形往往是大有裨益的。我们规定对于 n n n个盘子来说,该问题的最优解为 T n T_n Tn
- n = = 1 : n==1: n==1: 显然最少的步骤数为 T 1 = 1 T_1=1 T1=1
- n = = 2 : n==2: n==2: 先把最小的放到一个柱子上,然后把大的一个放到另一个柱子上,最后再把小的放到大的上面,显然最少的步骤数为 T 2 = 3 T_2=3 T2=3
- n = = 3 : n==3: n==3: ,手动推导一下,显然最少的步骤数为 T 3 = 7 T_3=7 T3=7
2、现在我们来研究盘子数为 n n n的问题的解,从上面的小的情形可以知道,问题的解决思路就是 先把除了最大的所有的盘子都移动一个柱子上,耗费 T n − 1 T_{n-1} Tn−1步骤数,然后 再把最大的那个盘子放到另一个柱子上 ,需要一步,最后再把其他盘子想办法转移动最大盘子所在的柱子上面,耗费 T n − 1 T_{n-1} Tn−1步骤数。所以可以得到:
T n ≤ 2 ∗ T n − 1 + 1 T_n \le 2*T_{n-1}+1 Tn≤2∗Tn−1+1
为什么是小于等于呢?这里其实是严谨的,对于上面的思路来说,我们只是证明了上限,即使用这个思路的话我们最多需要多少步骤,但是我们并没有证明这个思路一定是最少的方法,如果还有更好的方法呢? 实际上并没有,因为必定要走的一步是把最大的盘子放到一个空的柱子上,而在这之前至少需要 T n − 1 T_{n-1} Tn−1步,而在这之后也就至少需要把剩下的盘子转移到最大的盘子所在的柱子上。因此有:
T n ≥ 2 ∗ T n − 1 + 1 T_n \ge 2*T_{n-1}+1 Tn≥2∗Tn−1+1
综合上面的讨论可以知道,我们可以得到一个递推关系表达式,这里我是用《具体数学》里面的叫法,叫做递归式:
T n = 2 ∗ T n − 1 + 1 T_n = 2*T_{n-1}+1 Tn=2∗Tn−1+1
这是一个很重要的式子,这表明所有规模的问题都有解,并且可以通过连续的递归得到。那么什么是递归?隐约中,能不能感受到递归的思想?有的人很喜欢区别递归和递推,其实在本质上它们二者有着巨大的联系。下面开始讨论。
// 递归的问题写法
long long cal(long long x) {
if(x == 1) return 1;
return 2 * cal(x - 1) + 1;
}
// 递推写法,我更喜欢叫做迭代
t[1] = 1;
for(int i = 2; i <= n; i++) {
t[i] = 2 * t[i - 1] + 1;
}
那么从上面的代码可以看出:
递归的本质:把大规模的问题转换为小规模问题进行求解,自顶向下。
递推的本质:自底向上,从小的问题开始向上求解规模大的问题。
共同的特点:问题的求解具有状态可转移的特性,即问题的求解并不是绝对独立的。
个人觉得动态规划的本质也是属于这些问题,递归和递推分别表示了两种不同的写法:搜索和迭代刷表。其实我个人认为,它们都是基于分治的思想。
二、平面直线问题(LINES IN THE PLANE)
这个问题很简单:平面上 n n n条直线最多能够将平面划分为多少个区域?
1、对于这个问题,我们还是先研究小的情形。我们规定对于 n n n条直线来说,该问题的最优解为 T n T_n Tn
- n = = 0 : n==0: n==0: 显然区域的数量为 T 0 = 1 T_0=1 T0=1
- n = = 1 : n==1: n==1: 显然区域的数量最多为 T 1 = 2 T_1=2 T1=2
- n = = 2 : n==2: n==2: 两条直线相交,最多可以得到区域数量 T 2 = 4 T_2=4 T2=4
- n = = 3 : n==3: n==3: 三条直线两两相交,手动推导一下,显然最多的区域数为 T 3 = 7 T_3=7 T3=7
2、现在我们来研究盘子数为 n n n的问题的解,从上面的小的情形可以知道,问题的解决思路就是 每一次加上的线都尽量去与所有已有的直线相交,这样可以切割更多的区域,对于问题规模为 n n n的时候而言,当且仅当第 n n n条直线与已有的 n − 1 n-1 n−1条直线相交时,可以切割出多的 n n n个区域。那么可以得到:
T n ≤ T n − 1 + n T_n \le T_{n-1}+n Tn≤Tn−1+n
事实上,对于第 n n n条直线,我们保证取一个位置,使得它与已有的直线都不平行,那么可以取等号。即:
T n = T n − 1 + n T_n = T_{n-1} + n Tn=Tn−1+n
至此,该问题已经得到了解答,可以发现解题的思路和hanoi塔的问题思路一模一样,都是基于递归式求解。
三、约瑟夫问题(THE JOSEPHUS PROBLEM)
这个问题非常巧妙,相传是在一个洞穴内,数十名反抗者不想被敌人杀死,决定自杀的故事。所有的人都围坐成一个圆圈,每隔一个人就自杀一次,约瑟夫不想死,他一下子就找到了幸存者的位置。
1、对于这个问题,我们还是先研究小的情形。我们规定对于 n n n个人来说来说,该问题的幸存者编号为 T n T_n Tn
假设这里有 11 11 11个人:
第一轮死掉的人分别是:
2
,
4
,
6
,
8
,
10
,
1
2, 4, 6, 8, 10, 1
2,4,6,8,10,1
第二轮死掉的人为:
3
,
7
,
11
3, 7, 11
3,7,11
第三轮,已经可以知道幸存者的编号就是
5
5
5,因为接下来死的是
9
9
9。
2、现在我们来研究人数为 n n n的问题的解,从上面的小的情形可以知道,问题的解决思路就是 每一轮刷人之后,剩下的人会组成一个新的约瑟夫环,只是编号有点不同,但是只要做适当的变换,就可以发现一个奇妙转换,观察下面的两幅图片,那么可以得到:
- 3 = 2 ∗ 1 + 1 3 = 2*1 + 1 3=2∗1+1
- 5 = 2 ∗ 2 + 1 5 = 2*2 + 1 5=2∗2+1
- 7 = 2 ∗ 3 + 1 7 = 2*3 + 1 7=2∗3+1
- 9 = 2 ∗ 4 + 1 9 = 2*4 + 1 9=2∗4+1
- 11 = 2 ∗ 5 + 1 11 = 2*5 + 1 11=2∗5+1
显然下面的约瑟夫环的解为 T = 2 ∗ T 5 + 1 T=2*T_5+1 T=2∗T5+1
事实上,对于人数为偶数的情况,有递归式:
T 2 n = 2 ∗ T n − 1 T_{2n} = 2*T_n - 1 T2n=2∗Tn−1
事实上,对于人数为奇数的情况,有递归式:
T 2 n + 1 = 2 ∗ T n + 1 T_{2n+1} = 2*T_n +1 T2n+1=2∗Tn+1
至此,该问题已经得到了解答,可以发现解题的思路和上面的两个问题思路一模一样,都是基于递归式求解。