理解递归函数,和动态规划

作为一个CS学生,必须理解递归函数。这学期的Foundation of Computer里有一章讲到了递归,突然间有了新感悟。

以前理解递归,总会在脑海里模拟递归过程,直到返回为止。但常常内存不够,大脑宕机……T.T

------------------------------------------------------------------------------------------------------------------------------------

举两个例子,一下就明白了:

a[n] = a[n-1] + 1

a[0] = 0;

等差数列!…………一个数的值等于前一个数加1.再加上个初始条件。

F[n] = F[n-1] + F[n-2]

F[0] = 0  F[1] = 1

Fibonacci 数列 ……… 这个数等于前两个数之和。初始条件是前两个数。

还有高中那些个数列啊什么的

a[n] = Aa[n-1] + B , a[0] = xxx。依稀记得求个特征值什么的。

我们知道了递归,其实是定义相关关系。

实践一下:

一、求a[1...n]的最大值

定义max(n)是数组的最大值(注意它虽然是一个递归函数,但实际还是一个值,和前面提到的例子中的F[n],a[n]是一样样的)

max(n) = a[n] 和 max(n-1) 两数较大的那一个

max(1) = a[1]

第一:定义相关关系。

第二:定义截至条件。

相关代码:

int max( int a[], int n){
    if( n == 0 ) return a[0];
    return a[n] > max( a, n-1 )? a[n]:max(a,n-1);
}

二、生成组合数

 a1a2......an选m个数,C(a1a2....an,m)代表选m个数的结果

C(a1...an,m) = a1C(a2...an,m-1) + a2C(a3...an,m-1)  +...+ a[n-m+1]C(an-m+2...an,m-1)

截至条件:没有可能时返回,即, 数列长度小于m或者m<=0

举例来说: 1 2 3 4选3个数的组合,等于 1{2 3 4 选2个数的组合} + 2{3 4 选2个数的组合} + 3 { ... 这里没有选择就返回了 }。

其中

2 3 4选2个数的组合,等于2{3 4选1个数的组合} + 3{4 选1个数的组合}。

3 4选2个数的组合,等于3{4 选1个数的组合}。

……(不展开了)

所以1 2 3 4选3个数的组合等于

1{2 3,2 4} + 2{ 3 4 } = 1 2 3 + 1 2 4 + 2 3 4

相关代码:

template<typename T>
void PrintVV(vector<vector<T>> v){
    for (auto it : v){
        for (auto it2 : it){
            printf("%d ", it2);
        }
        printf("\n");
    }
}

void comb(vector<vector<int>> &vviInsert, vector<int> &a, int m, int pos/* start pos */){
    if (a.size() - pos < m || m <= 0){
        return;
    }
    if (!vviInsert.size()){
        for (int i=0; a.size()- i >= m; i++){
            vviInsert.push_back(vector<int>({ a[i] }));
            comb(vviInsert, a, m-1, i + 1);
        }
    } else{
        vector<int> aVi = vviInsert.back();
        vviInsert.pop_back();
        for (int i=pos; a.size() - i >= m; i++){
            aVi.push_back(a[i]);
            vviInsert.push_back(aVi);
            aVi.pop_back();
            comb(vviInsert, a, m-1, pos + 1);
        }
    }
}

int main(int argc, char *argv[])
{
    vector<int> viA;
    vector<vector<int>> vviResult;
    viA.assign({ 1, 2, 3,4 });
    comb(vviResult, viA, 2, 0);
    PrintVV(vviResult);
    system("pause");
    return 0;
}

总结一下,设计递归函数一要理出相关关系。二要设置截至条件。

多说一句,相关关系实际是问题与子问题的关系,可能会重复计算,比如在生成组合数的例子中,我们看到绿色部分被重复计算了两次。而在寻找Fibonacci数中,我们知道重复计算量会更大。对于这种需要重复计算的子问题,可以把重复计算子问题的答案储存起来,使用时查询,无需计算。这就是动态规划的思想。

时间: 2024-08-09 19:42:30

理解递归函数,和动态规划的相关文章

深入理解递归函数的调用过程

转:深入理解递归函数的调用过程 下面是个关于递归调用简单但是很能说明问题的例子: 1 /*递归例子*/ 2 #include<stdio.h> 3 void up_and_down(int); 4 int main(void) 5 { 6 up_and_down(1); 7 return 0; 8 } 9 void up_and_down(int n) 10 { 11 printf("Level %d:n location %p/n",n,&n); /* 1 */

简单理解算法篇--动态规划

动态规划方法通常用来求解最优化问题,这些问题有很多种解,但我们希望寻求最优解. 满足两个条件既可以使用动态规划 1.具有最优子结构 2.子问题重叠 至于这两点是什么意思?先看个问题 现在有个钢筋长度和价格对应的表,问:给你个长度为n的钢筋怎么卖最划算? 长度 1 2 3 4  5   6  7   8   9  10 价格 1 5 8 9 10 17 17 20 24 30 现在就是要把所有的切法都遍历一遍,找出最划算的切法,当你把钢筋切了一刀后,是不是变成了两段?那就要考虑的就是这两段怎么切最

【转】深入理解递归函数的调用过程

下面是个关于递归调用简单但是很能说明问题的例子: [cpp] view plain copy /*递归例子*/ #include<stdio.h> void up_and_down(int); int main(void) { up_and_down(1); return 0; } void up_and_down(int n) { printf("Level %d:n location %p/n",n,&n); /* 1 */ if(n<4) up_and_

帮助大家理解一下递归函数的返回值...

如题: 递归函数往往可以简化我们的代码,尤其是对树的遍历和利用回溯算法写代码的时候,但是递归函数的返回值往往是困扰我们的. 总体来说,我们先要理解函数的调用过程,函数调用过程会用栈来保存函数的返回值和过程,而递归函数就是调用自身函数的过程,所以也是用栈存储,这样就比较容易理解了. 下面一段代码可以帮助大家理解递归函数的返回值. 1 package test; 2 3 4 public class RecursionValueReturn { 5 6 /* 7 *目的: 8 * 递归返回值测试 9

关于动态规划的理解

动态规划是个比较有趣的算法,第一次接触动态规划也是从一个比较特别的教程开始的,这里贴出原文地址http://blog.csdn.net/woshioosm/article/details/7438834 看完原文回到这里,其实我觉得很多像我这种C语言刚刚入门的人,只理解到了动态规划的转移方程,以原文中的例子为例: 当mineNum = 0且people >= peopleNeeded[mineNum]时 f(people,mineNum) = gold[mineNum] 当mineNum = 0

php利用递归函数实现无限级分类

相信很多学php的很多小伙伴都会尝试做一个网上商城作为提升自己技术的一种途径.各种对商品分类,商品名之类的操作应该是得心应手,那么就可以尝试下无限级分类列表的制作了. 什么是无限级分类? 无限级分类是一种分类技巧,例如部门组织,文章分类,学科分类等常用到无限级分类,将其简单理解成分类就好了.其实我们仔细想一下,生活中的分类简直太多了,衣服可以分为男装和女装,也可以分为上衣和裤子,也可以根据年龄段分类.分类无处不在,分类显得“无限”.我这里就不说无限分类的必要性了. 无限级分类原理简介 无限分类看

Javascript 中的回调函数和递归函数简单实际分析学习

Javascript 中的回调函数和递归函数简单实际分析学习 1 回调函数 所谓回调函数简单理解就是将一个函数做为参数传递给其他的函数供其使用.(只是在js中,因为其它的语言中有指针这个概念). 举一个简单的例子,当我们在统计账单的时候就要整理材料这些,然后就需要计算器,计算器我们想象成为一个可以实现计算的函数.统计账单是另外的另一个函数,当统计账单的时候就会需要计算器这个函数的支持,其实这就是一个简单的回调.可以按这个理解. 下来我借用网上的一个例子: //先定义一个函数fun1 functi

算法导论_第十六章_动态规划_creatshare分享会

动态规划 注:该篇为本人原创,转载请注明出处:http://blog.csdn.net/chudongfang2015/article/details/51590817--开心 -.- 个人对动态规划的理解: 1.动态规划是一个付出额外空间来节省时间,就是所谓的空间换时间. 2.动态规划储存每个状态的最优解. 3.动态规划是用来把子问题的结果储存下来,再次用到的时候就不必再进行重复计算. 算法导论对动态规划的解释: 动态规划和分治方法相似,都是通过组合子问题的解来求解原问题,分治方法将问题划分为

【上海交大oj】能量项链(动态规划)

1073. 能量项链 Description 在Mars星球上,每个Mars人都随身佩带着一串能量项链.在项链上有N颗能量珠.能量珠是一颗有头标记与尾标记的珠子,这些标记对应着某个正整数.并且,对于相邻的两颗珠子,前一颗珠子的尾标记一定等于后一颗珠子的头标记.因为只有这样,通过吸盘(吸盘是Mars人吸收能量的一种器官)的作用,这两颗珠子才能聚合成一颗珠子,同时释放出可以被吸盘吸收的能量.如果前一颗能量珠的头标记为m,尾标记为r,后一颗能量珠的头标记为r,尾标记为n,则聚合后释放的能量为m×r×n