算法实战之递归(2)(递归算法深层次总结)
2014-04-23 16:54
316 查看
大家好,这两天时间比较充裕,我可以有很多的时间来写代码,真是一件幸福的事,时间比较多,可浪费的时间也比较多,等这个星期忙完了,我也要开始我的coursea之旅了,废话不多说了,直接进入主题——递归算法的总结。
先从两道题目说起:
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 5660 Accepted Submission(s): 1755
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?
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)
For each test case, there is only one integer means the number of queue satisfied the headmaster’s needs.
这道题目一入手,我就开始比划了,我还没有想到为什么要用到递归,就顺着题目的思路,希望可以直接求解,就这样一股脑的写出了下面的代码,猛然发现写不下去了。
我真的是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
最后附上代码:
提供另外一道题目供大家欣赏,
Problem Description
人称“AC女之杀手”的超级偶像LELE最近忽然玩起了深沉,这可急坏了众多“Cole”(LELE的粉丝,即"可乐"),经过多方打探,某资深Cole终于知道了原因,原来,LELE最近研究起了著名的RPG难题:
有排成一行的n个方格,用红(Red)、粉(Pink)、绿(Green)三色涂每个格子,每格涂一色,要求任何相邻的方格不能同色,且首尾两格也不同色.求全部的满足要求的涂法.
以上就是著名的RPG难题.
如果你是Cole,我想你一定会想尽办法帮助LELE解决这个问题的;如果不是,看在众多漂亮的痛不欲生的Cole女的面子上,你也不会袖手旁观吧?
Input
输入数据包含多个测试实例,每个测试实例占一行,由一个整数N组成,(0<n<=50)。
Output
对于每个测试实例,请输出全部的满足要求的涂法,每个实例的输出占一行。
这个我就不分析了,直接贴上代码,大家感受一下:
文章写到这里并没有结束,还有递归的深层次总结,知识方面的东西,可能比题目更好理解一些,在看完上面两道题目之后,再来看这些知识,感觉就是不一样。
通过二叉树来体会,我觉得是最好的理解方法了,不足之处,希望大家多多指正,多多留言!
先从两道题目说起:
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; }
文章写到这里并没有结束,还有递归的深层次总结,知识方面的东西,可能比题目更好理解一些,在看完上面两道题目之后,再来看这些知识,感觉就是不一样。
通过二叉树来体会,我觉得是最好的理解方法了,不足之处,希望大家多多指正,多多留言!
相关文章推荐
- 算法总结(11)--伪递归,dfs,动态规划题,需要转换下思路
- 【算法导论】贪心算法,递归算法,动态规划算法总结
- 贪心算法,递归算法,动态规划算法比较与总结
- 【算法导论】贪心算法,递归算法,动态规划算法总结
- 无聊时总结总结算法之01递归
- 有关递归的三道算法题总结
- 算法设计课程总结1~2(渐进分析记号,复杂性比较,递归,分治)
- 算法总结(7)--leetcode上的递归,BFS,DFS思考
- 【算法导论】贪心算法,递归算法,动态规划算法总结
- 算法实战学习之递归(1)
- 【算法】递归与尾递归总结
- 黑马程序员 《ios零基础教程》 --补齐算法、宏定义、typedef、递归 2014-4-20总结
- 二叉树的遍历算法(先序中序后序遍历的递归算法与非递归算法、层级遍历的递归与非递归算法)
- 【算法导论】贪心算法,递归算法,动态规划算法总结
- 【算法小总结】二分图最大匹配的非递归方法
- 关于算法递归的总结
- 递归和分治策略的算法总结
- 图的BFS算法和DFS的递归非递归算法
- 机器学习实战-基本算法总结1
- 递归算法与迭代算法总结