递归习题整理
1、子集问题
求n个正整数构成的一个给定集合A = {a1,a2,a3,...,an}的子集,子集的和要等于一个给定的正整数d。请输出所有符合条件的子集。
解题思路:
1)从原始集合中分离出一个元素,它有两种选择:选择放入接轨集合,或者不放入结果集合;
2)对于剩下的集合,重复1的动作,直到原始集合为空集,证明所有子集已经选取完成了;
/** * 子集问题 * @param soFar 当前原始集合还存在的数字 * @param rest 存放结果的集合 */ void RecSubsets(string soFar[], int rest[]) { if(rest == " ") cout << soFar << endl; else { RecSubset(soFar + rest[0], rest.substr(1)); RecSubset(soFar, rest.substr(1)); } }
2.排列问题
求n个正整数构成的一个给定集合A = {a1,a2,a3,...,an}的全排列
1)输入串中选择一个加入输出串
2)剩下的输出串中在加一个到输出串;知道输入串为空
/** * 排列问题 * @param soFar 已经被选择的字母 * @param rest 没有被选择的字母 */ void RecPermute(string soFar, string rest) { if(rest == " ") cout << soFar << endl; // 所有字母都已经被选择了 else { for(int i = 0; i < rest.length(); i++) //对于没有被选择的字母,每一个都有可能被选择 //可以将当前被选取的字母与最前面的交换位置,这样每次只要去掉第一个即可 { string next = soFar + rest[i]; string remain = rest.substr(0,i) + rest.substr(i+1); RecPermute(next, remain); } } }
子集问题和排列问题可以说是递归的两个最为基础重要的问题了,子集问题是分离重复,而排列问题是选择重复,理解这两个递归,对我们认识递归有很重要的作用。
递归回溯子路简单整理:
bool solve (configuration conf) { if(no more choice) return (conf is goal state); else { for(all avaiable choices) { try one choice c; if( solve(conf with choice c made)) return true; unmake choice c; } return false } }
3、RPG难题
有排成一行的n个方格,用红(Red)、粉(Pink)、绿(Green)三色涂每个格子,每格涂一色,要求任何相邻的方格不能同色,且首尾两格也不同色.求全部的满足要求的涂法.
解题思路: 子集问题
数组 f[i] 存放i个方格有多少种涂法
n个方格可以由n-1个方格和n-2个方格填充得到
1)在一涂好的n-1个格子里最后再插入一个格子,就得到了n个格子了。因为已经填好n-1的格子中,每两个格子的颜色都不相同。所以只有1种填法。
这种情况下 f(n) 一种有f(n-1)种填法。
2)假如前面 n-2个合法 ,但是第n-1个不合法 ,即第n-1个和第一个颜色一样,这个时候最后一个格子颜色可以有2种
这种情况下f(n) 有 2*f(n-2)种填法。
所以 f(n) = f(n-1) + 2* f(n-2)
4、EOF串
长度为n的字符串只由 EOF 组成,且O和O不相邻,求所有满足要求字符串个数。
解题思路:
分为2种情况讨论,最后是O最后不是O
把末尾是O的情况保存在D[i][0]里面,把最后不是O的保存在D[i][1]里;
在原来的字符串加上 O要得到合法字符串,末尾必然不为O,即D[n][0] = D[n-1][1];
在原来字符串加上非O要得到合法字符串,末尾随意,即D[n][1] = D[n-1][0] + D[n-1][1];
D[1][0] = 1; D[1][1] = 2;
5.抽奖概率
输入数据的第一行是一个整数C,表示测试实例的个数,然后是C 行数据,每行包含一个整数n(1<n<=20),表示参加抽奖的人数。 求没有人中奖的概率
解题思路:
错排:
N张票的所有排列可能自然是Ann = N!种排列方式,现在分析N张票排错的情况:
如果前面N-1个人拿的都不是自己的票,即前N-1个人满足错排,现在又来了一个人,他手里拿的是自己的票。只要他把自己的票与其他N-1个人中的任意一个交换,就可以满足N个人的错排。这时有N-1种方法。
如果前N-1个人不满足错排,而第N个人把自己的票与其中一个人交换后恰好满足错排。这种情况发生在原先N-1人中,N-2个人满足错排,有且仅有一个人拿的是自己的票,而第N个人恰好与他做了交换,这时候就满足了错排。
为前N-1个人中,每个人都有机会拿着自己的票。所以有N-1种交换的可能。
f(n) = (i - 1) * [f(n - 1) + f(n - 2)]
6.考新郎
首先,给每位新娘打扮得几乎一模一样,并盖上大大的红盖头随机坐成一排;
然后,让各位新郎寻找自己的新娘.每人只准找一个,并且不允许多人找一个.
假设一共有N对新婚夫妇,其中有M个新郎找错了新娘,求发生这种情况一共有多少种可能.
方法同上,可以自己思考一下
7.折线分割平面
我们看到过很多直线分割平面的题目,今天的这个题目稍微有些变化,我们要求的是n条折线分割平面的最大数目。比如,一条折线可以将平面分成两部分,两条折线最多可以将平面分成7部分,具体如下所示。

解题思路:
假如是直线分割:N条相交的直线最多能把平面分割成几块。
添加第n条直线,平面最多的情况是 第n条直线与前面 n-1条直线都相交,切没有三条直线交于一点。这样,第n条直线一共有n-1个交点。我们知道,增加n个焦点,则增加n+1个平面。
所以 n条直线分割平面最大数是:
1 + 1 + 2 + 3 + ... + n = (n2 + n + 2) / 2
假如每次增加的不是一条直线,而是一对平行线:
当第N次添加时,前面已经有2N-2条直线了,第N次添加时,第2N-1条直线和第2N条直线各能增加2(n-1)+1个平面。所以第N次添加增加的面数是2[2(n-1) + 1] = 4n - 2 个,总面数等差数列计算 应该是1 + 4n(n+1)/2 - 2n = 2n2 + 1 。
如果,每次加进来的平行边换成一头相交的折线:
我们看到,平面1、3已经合为一个面,既少了一个面。因此,每当一组平行线相交后,就会减少一个面。
因此,本题所要求的折线分割平面,自然就是上面求的的平行线分割平面数减去N。即 f(n) = 2n2 - n + 1;
浙公网安备 33010602011771号