递归习题整理

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;

posted @ 2018-03-13 15:20  丶潇  阅读(1263)  评论(0)    收藏  举报