1,关键词解释

1.1 暴力递归:

1, 把问题转化为规模缩小了的同类问题的子问题

2, 有明确的不需要继续进行递归的条件(base case)

3, 有当得到了子问题的结果之后的决策过程

4, 不记录每一个子问题的解

1.2 动态规划:

1, 从暴力递归中来

2, 将每一个子问题的解记录下来,避免重复计算

3, 把暴力递归的过程,抽象成了状态表达

4, 并且存在化简状态表达,使其更加简洁的可能

2,学会尝试才能掌握

2.1 P类问题和NP问题

P类问题:时间复杂度为多项式; 知道怎么算,让计算机帮我算。

NP问题:时间复杂度很复杂,指数级或位置; 不知道怎么算,但是知道怎么尝试。

2.2 尝试的重要性

学会了尝试,在不断的实践中积累经验才能真正掌握这些算法思想的精髓。许多本科毕业生甚至研究生,都缺乏这种能力。

3,例题实践

3.1 求n!的值

#include <iostream>

using namespace std;

class Factorial {
public:
//方法一:递归版
int factor(int n) {
if (n<) return -; if (n == || n == ) return ;
else {
return n*factor(n - );
} }
//方法二:直接法
int factor2(int n) {
int res=;
for (int i = ; i <= n; ++i) {
res *= i;//1×2×3×……×n
}
return res;
}
}; int main(){
Factorial test;
//cout << test.factor(3) << endl;
cout << test.factor2()<<endl;
return ;
}

3.2 汉诺塔问题

问题描述:

 三根柱子:"left","mid","right"
要求:
1.要把放在“left”杆子上的n个从大到小叠加放置的圆盘移动到“right”杆子上;
2.移动过程中,一个只能移动一个圆盘,且大的圆盘不能放置在小的圆盘上。

递归分解:

    1. n为1时,"left"杆子上只有一块圆盘,可以直接将它移动至"right"杆子上;(base case:递归出口)
    2. n大于1时,要想使得第1步成立,要先把"left"上面的n-1块圆盘移动至辅助的"mid"杆子上;
    3. 最后,将"mid"杆子上的n-1块圆盘移动至"right"杆子上。
//题目地址:https://www.nowcoder.com/questionTerminal/7d6cab7d435048c4b05251bf44e9f185 

class Hanoi {
public:
vector<string> getSolution(int n) {
//判断n是否合法输入
if(n<=) return res;
func(n,"left","mid","right");
return res;
} void func(int n, string from, string mid,string to){
if(n==)
res.push_back("move from "+from+" to "+to);
else{
func(n-,from,to,mid);
func(,from,mid,to);
func(n-,mid,from,to);
}
} private:
vector<string> res;
};

3.3 打印一个字符串(“abc”)的所有的子序列(注:不是子串)

a:选或不选   b:选或不选 c:选或不选  2×2×2=8种可能,包含空子序列。
#include <iostream>
#include <string> using namespace std; //void printAllSbu(char str[],int i,string res)
void printAllSub(char *str,int i, string res) {
//str[]字符串数组的实际大小为sizeof(str),有效大小为sizeof(str)-1
if (i == sizeof(str)-) {
std::cout << res;
return;
}
else {
printAllSub(str, i + , res);//不选str[i]
printAllSub(str, i + , res + str[i]);//选str[i]
} } void main() {
string test = "abc";
char v[];
//strncpy_s 优化后更安全的函数
strncpy_s(v, test.c_str(), test.length() + );//必须加1,\0还占一个位置
printAllSub(v, , " ");
}

3.4 打印一个含n个字母的字符串(如:“abc”)的所有的全排列

#case1:假设不含重复字母

递归思路:

    1. (bese case:) n=1时,如果字符串中只有一个元素,直接生成全排列;
    2. 当n>1时,如果能生成n-1个元素的全排列,就能生成n个元素的全排列,以三个字符"abc"为例:
    • 首先我们固定第一个字符a,排列后面的两个字符bc;
    • 当两个字符bc排列求好后,我们把字符b和第一字符a交换,使得b固定在第一个字符,排列后面的两个字符ac;
    • 当两个字符ac排列求好后,我们把字符c和第一字符a交换,使得c固定在第一个字符,排列后面的两个字符ab;这里特别需要注意一点,我们上一步交换了a和b的位置,要想保证我们正确的交换a和c的位置,需要恢复原字符串,先换回a和b的位置。因为,我们确定第一个字符串和后面每一个字符串交换,固定第一个位置的元素时,是基于初始字符串abc的次序考虑的。(参考资料的第一篇博文中存在代码错误就是这里没有先恢复原串)

  既然我们已经知道怎么求三个字符的排列,那么固定第一个字符之后求后面两个字符的排列,就是典型的递归思路了

/*打印一个字符串中所有字母的全排列(假设不含重复字母))*/

#include <iostream>
#include <string> using namespace std; void printAllArr(char str[],int i) {
int n = sizeof(str)/sizeof(str[])-;//获取数组长度 sizeof(str)/sizeof(str[0])
//cout << n << endl; if (i == n-) {
for(int w = ; w < n;++w){
cout << str[w];
}
cout << endl;
return;
}
else {
for (int j = i; j <n; ++j) {
swap(str[i], str[j]);
printAllArr(str, i + );
swap(str[i], str[j]);//没有这一行,结果会出错。
}
} } void main() {
string test = "abc";
char v[];
strncpy_s(v, test.c_str(), test.length() + );//必须加1,\0还占一个位置
printAllArr(v, );
}

#case2:含有重复字母,且要求输出结果无重复

关键思路:如果str[i]和str[j]相同,则忽略交换。(如:"abca",遇到第一个a和第四个a则不交换。)

#include <iostream>
#include <string> using namespace std; //判断是否需要交换
int is_swap(char *str, int begin, int k) {
int i, flag; for (i = begin, flag = ; i < k; i++) {
if (str[i] == str[k]) {
flag = ;
break;
}
}
return flag;
} //打印所有的全排列
void printAllArr(char str[], int i, int n) {
/*--------不能在这里获取正确的数组长度----------*/
//int n = sizeof(str) / sizeof(str[0]);//遗留问题:这里为什么固定是4呢?
//cout << n << endl; //原因:这里参数传递只是数组的首元素指针(32位的内存地址),并不是整个数组,所有固定是4
//cout << sizeof(str) << endl; //4
//cout << sizeof(str[0]) << endl; // if (i == n - ) {
for (int w = ; w < n; ++w) {
cout << str[w];
}
cout << endl;
return;
}
else {
for (int j = i; j < n; ++j) {
if (is_swap(str, i, j)) {//判断是否需要交换,相同则不交换
swap(str[i], str[j]);
printAllArr(str, i + ,n);
swap(str[i], str[j]);
}
}
}
} void main() {
char str[] = { 'a','b','c','a'};
int length = sizeof(str) / sizeof(str[]);//获取数组长度
//cout <<"length = " <<length << endl;
printAllArr(str, , length);
}

参考资料:

1.输出一个字符串的全排列 (注:该文提供的代码结果有误,少了上面标红的一行代码)

2.字符串 全排列生成问题

3. https://www.nowcoder.com/courses/semester/senior 《牛客高级项目课——(牛客网)》--大牛·左程云

Idea 02.暴力递归与动态规划(1)的更多相关文章

  1. 【LeetCode】300.最长递增子序列——暴力递归(O(n^3)),动态规划(O(n^2)),动态规划+二分法(O(nlogn))

    算法新手,刷力扣遇到这题,搞了半天终于搞懂了,来这记录一下,欢迎大家交流指点. 题目描述: 给你一个整数数组 nums ,找到其中最长严格递增子序列的长度. 子序列是由数组派生而来的序列,删除(或不删 ...

  2. 算法 递归 迭代 动态规划 斐波那契数列 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  3. 70. Climbing Stairs【leetcode】递归,动态规划,java,算法

    You are climbing a stair case. It takes n steps to reach to the top. Each time you can either climb ...

  4. 面试题目——《CC150》递归与动态规划

    面试题9.1:有个小孩正在上楼梯,楼梯有n个台阶,小孩一次可以上1阶.2阶或者3阶.实现一个方法,计算小孩有多少种上楼梯的方式. 思路:第4个数是前三个数之和 注意:能不能使用递归,能不能建立一个很大 ...

  5. java暴力递归回溯算法

    今天这个问题是我之前一直想解决的,还记得以前第一次上蓝桥杯的课的时候,也就是大一高数期中模拟考试那天,下午去上蓝桥杯课,遇到这道题,当时写了写,根本没有思路,然后就给大一的模拟考试去了.印象深刻啊,一 ...

  6. python编写PAT 1007 Maximum Subsequence Sum(暴力 分治法 动态规划)

    python编写PAT甲级 1007 Maximum Subsequence Sum wenzongxiao1996 2019.4.3 题目 Given a sequence of K integer ...

  7. OptimalSolution(1)--递归和动态规划(1)斐波那契系列问题的递归和动态规划

    一.斐波那契数列 斐波那契数列就是:当n=0时,F(n)=0:当n=1时,F(n)=1:当n>1时,F(n) = F(n-1)+F(n-2). 根据斐波那契数列的定义,斐波那契数列为(从n=1开 ...

  8. 最大子序和:暴力->递归->动规->线段树

    题目描述 给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和. LeetCode:53. 最大子序和 题解 显而易见的暴力解法 最容易想到的便是暴力穷 ...

  9. C#递归、动态规划计算斐波那契数列

    //递归         public static long recurFib(int num)         {             if (num < 2)              ...

随机推荐

  1. LeetCode 96——不同的二叉搜索树

    1. 题目 2. 解答 以 \(1, 2, \cdots, n\) 构建二叉搜索树,其中,任意数字都可以作为根节点来构建二叉搜索树.当我们将某一个数字作为根节点后,其左边数据将构建为左子树,右边数据将 ...

  2. C语言链接数据库

    一.解释一下函数功能和用法 1.mysql_real_connect 函数原型:MYSQL *mysql_real_connect(MYSQL *mysql, const char *host, co ...

  3. 1.编译azkaban

    1.下载azkaban的源码 https://github.com/azkaban/azkaban.git 然后解压得到azkaban-master.zip,解压:unzip azkaban-mast ...

  4. 卸载CDH5.7

    CDH5.7卸载1.记录用户数据目录2.关闭所有服务2.1在CM中,选择某个集群,然后停止集群.2.2逐个关闭CDH中的服务3.删除parcels4.删除集群5.卸载Cloudera manager ...

  5. POJ 3693 Maximum repetition substring(后缀数组)

    Description The repetition number of a string is defined as the maximum number R such that the strin ...

  6. M2迭代分数分配

    M2中仍然依据每个成员的工作量,贡献度分配相应得分. 成员 得分 申开亮 25 王皓南 24 许晋 21 黄玉冰 20 王宇杰 18 吴润凡 17 巴丹益昔 15

  7. Python基础1 Hello World!

    从今天开始和大家分享一下python最基础的知识,以便帮助初学者快速入门. 最最基础的当然是hello world 了,无论哪门语言都会从它开始... 简单的‘Hello World!’ 1. 直接运 ...

  8. Java中 Auto-boxing/unboxing

    Java 中 Auto-boxing/unboxing 机制,在合适的时机自动打包,解包. 1. 自动将基础类型转换为对象: 2. 自动将对象转换为基础类型: Demo_1: import java. ...

  9. LintCode-212.空格替换

    空格替换 设计一种方法,将一个字符串中的所有空格替换成 %20 .你可以假设该字符串有足够的空间来加入新的字符,且你得到的是"真实的"字符长度. 你的程序还需要返回被替换后的字符串 ...

  10. bootstrap控件点击之后没有反应的原因

    引用的jquery.js文件要放到bootstrap.js的前面 jquery.js文件版本太低. 这些问题可以通过firebug或者谷歌调试器发现. 问题很简单,简单记录下,以免以后遗忘.