算法实战之递归(2)(递归算法深层次总结)

大家好,这两天时间比较充裕,我可以有很多的时间来写代码,真是一件幸福的事,时间比较多,可浪费的时间也比较多,等这个星期忙完了,我也要开始我的coursea之旅了,废话不多说了,直接进入主题——递归算法的总结。

先从两道题目说起:

Children’s Queue

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 5660 Accepted Submission(s): 1755

Description

There are many students in PHT School. One day, the headmaster whose name is PigHeader wanted all students stand in a line. He prescribed that girl can not be in single. In other words, either no girl in the queue or more than one girl stands side by side. The case n=4 (n is the number of children) is like
FFFF, FFFM, MFFF, FFMM, MFFM, MMFF, MMMM
Here F stands for a girl and M stands for a boy. The total number of queue satisfied the headmaster’s needs is 7. Can you make a program to find the total number of queue with n children?

Input

There are multiple cases in this problem and ended by the EOF. In each case, there is only one integer n means the number of children (1<=n<=1000)

Output

For each test case, there is only one integer means the number of queue satisfied the headmaster’s needs.


这道题目一入手,我就开始比划了,我还没有想到为什么要用到递归,就顺着题目的思路,希望可以直接求解,就这样一股脑的写出了下面的代码,猛然发现写不下去了。

#include <iostream>
//还是有问题啊,出不来
//没看到递归的运用

using namespace std;

int a[1000];
int n =0;

int calculateFactorial(int p)
{
    int sum = 1;
    for(int i =0;i<=p;++i)
    {
        sum = sum * i;
    }
    return sum;
}

int calculateCombination(int m1,int m2)
{
    int a1 = calculateFactorial(m1);
    int a2 = calculateFactorial(m2;
    int a3 = calculateFactorial(m1-m2);
    return a1 / (a2 * a3);
}

int getCount(int p)
{
    int time = 0;
    int sum = 0;
    if(p%2 == 0)
    {
        time = p / 2;
    }
    else{
        int a1 = p - 3;
        int temp = a1;
        if(a1 == 0)
        time = 1;
        else{
            time = 1;
            sum += n - p + 1;
            for(int i =0;i<(a1/2);++i)
            {
                time += temp / 2;
                for(int j = 0;j<temp/2;++j)
                {
                    sum += calculateCombination(n-p+1,temp) * calculateFactorial(2);
                }
                temp = temp - 2;
            }
        }
    }
    return time;
}

int dealProcess(int p)
{
    int time = getCount(p);
    for(int i =0;i<time;++i)
    {

    }
}

int main()
{
    int sum = 0;
    cin >> n;
    for(int i =0;i<n;++i)
    {
        sum +=dealProcess(i);
    }
    return 0;
}

我真的是too yong,too naive,结果证明是行不通的,怎么想到要用递归呢?其实,这就是在面对一个过程异常复杂的题目时,何时使用递归,这是我们要问自己的,我看到了一篇博客(原文地址:http://blog.csdn.net/ggxxkkll/article/details/7524056)总结得很好,再结合自己的体会摘录如下:

递归算法的思想是:把规模大的较难解决的问题转化成规模较小的,以解决的同一问题,规模较小的问题又可以转化成规模更小的问题,直到可以直接得出他们的解,从而得到原问题的解

一个问题要采用递归方法来解决时,必须符合以下三个条件:
1.解决问题时,可以把一个问题转化为一个新的问题,而这个新的问题的解决方法仍与原问题的解法相同,只是所处理的对象有所不同,这些被处理的对象之间是有规律的递增或递减;
2.可以通过转化过程是问题得到解决;
3.必定要有一个明确的结束递归的条件,否则递归将会无止境地进行下去,直到耗尽系统资源。也就是说必须要某个终止递归的条件。如求阶乘问题,我们要求n的阶乘(n!),可以把这个问题转化为n*(n-1)!,而要求(n-1)!又可转化为(n-1)*(n-2)!,……,这里面都有一个一个数乘以另一个数阶乘的问题,被处理的对象分别是n,n-1,……,是有规律的递减。但是我们不能让程序无休止的乘下去,必须要给他一个结束条件,该问题恰好有一个结束条件,那就是当n=0时,0!=1。

我之所以上面的代码求不出来,就是因为原问题的过程复杂,不能直接求解,上面给出的采用递归解题的条件在我看来略显复杂,其实如果可以一句话总结的话,那就是:能很方便地写出递归表达式的题目采用递归算法都会异常简便

上面那道题目的递归表达式很不好理解,也应该是那道题的难点,我写一下我的理解:

F(n)表示n个人的合法队列,在具体分析之前,我们应该知道,合法队列+合法队列还应该是合法队列,但是不合法队列加上合法队列也可以是合法队列

按照最后一个人的性别分析,他要么是男,要么是女,所以可以分两大类讨论:

1、如果n个人的合法队列的最后一个人是男,则对前面n-1个人的队列没有任何限制,他只要站在最后即可,所以,这种情况一共有F(n-1);

2、如果n个人的合法队列的最后一个人是女,则要求队列的第n-1个人务必也是女生,这就是说,限定了最后两个人必须都是女生,这又可以分两种情况;

2.1、如果队列的前n-2个人是合法的队列,则显然后面再加两个女生,也一定是合法的,这种情况有F(n-2);

2.2、但是,难点在于,即使前面n-2个人不是合法的队列,加上两个女生也有可能是合法的,当然,这种长度为n-2的不合法队列,不合法的地方必须是尾巴,就是说,这里说的长度是n-2的不合法串的形式必须是“F(n-4)++,这是难点,我再解释一下,为什么这个不合法串的形式必须是这种形式,其实我上面提到了,这个不合法队列必须是能够加上一个合法队列变成合法队列的。

这种情况一共有F(n-4).

所以,通过以上的分析,可以得到递推的通项公式: F(n)=F(n-1)+F(n-2)+F(n-4)   (n>3)然后就是对n<=3 的一些特殊情况的处理了,显然:F(0)=1   (没有人也是合法的,这个可以特殊处理,就像0的阶乘定义为1一样)   F(1)=1    F(2)=2     F(3)=4


最后附上代码:

#include <iostream>

using namespace std;
int n = 0;

int recursion(int p)
{
    if(p == 1)
    return 1;
    else if(p == 2)
    return 2;
    else if(p == 3)
    return 4;
    else if(p == 4)
    return 7;
    else{
        return recursion(p-1)+recursion(p-2)+recursion(p-4);
        }
}

int main()
{
    cin >> n;
    cout << recursion(n);
    return 0;
}

提供另外一道题目供大家欣赏,

Problem Description
人称“AC女之杀手”的超级偶像LELE最近忽然玩起了深沉,这可急坏了众多“Cole”(LELE的粉丝,即"可乐"),经过多方打探,某资深Cole终于知道了原因,原来,LELE最近研究起了著名的RPG难题:

有排成一行的n个方格,用红(Red)、粉(Pink)、绿(Green)三色涂每个格子,每格涂一色,要求任何相邻的方格不能同色,且首尾两格也不同色.求全部的满足要求的涂法.

以上就是著名的RPG难题.

如果你是Cole,我想你一定会想尽办法帮助LELE解决这个问题的;如果不是,看在众多漂亮的痛不欲生的Cole女的面子上,你也不会袖手旁观吧?

 
Input
输入数据包含多个测试实例,每个测试实例占一行,由一个整数N组成,(0<n<=50)。
 
Output
对于每个测试实例,请输出全部的满足要求的涂法,每个实例的输出占一行。

这个我就不分析了,直接贴上代码,大家感受一下:

#include <iostream>

using namespace std;

int sum = 0;

int realRecursion(int n)
{
    if(n == 3)
    {
        return 6;
    }
    else if(n == 2)
    {
        return 6;
    }
    else if(n == 1)
    {
        return 3;
    }
    else{
        return realRecursion(n-1) + 2*realRecursion(n-2);
        }
}

int main()
{
    int n;
    cin >> n;
    sum = realRecursion(n);
    cout << sum;
    return 0;
}

文章写到这里并没有结束,还有递归的深层次总结,知识方面的东西,可能比题目更好理解一些,在看完上面两道题目之后,再来看这些知识,感觉就是不一样。







通过二叉树来体会,我觉得是最好的理解方法了,不足之处,希望大家多多指正,多多留言!


猜你喜欢

转载自blog.csdn.net/u011995233/article/details/24371981
今日推荐