zoukankan      html  css  js  c++  java
  • Idea 02.暴力递归与动态规划(1)

    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<0) return -1;
    
            if (n == 1 || n == 0) return 1;
            else {
                return  n*factor(n - 1);
            }
    
        }
        //方法二:直接法
        int factor2(int n) {
            int res=1;
            for (int i = 1; i <= n; ++i) {
                res *= i;//1×2×3×……×n
            }
            return res;
        }
    };
    
    int main(){
        Factorial test;
        //cout << test.factor(3) << endl;
        cout << test.factor2(4)<<endl;
        return 0;
    }

    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<=0) return res;
            func(n,"left","mid","right");
            return res;
        }
        
        void func(int n, string from, string mid,string to){
            if(n==1)
                res.push_back("move from "+from+" to "+to);
            else{
                func(n-1,from,to,mid);
                func(1,from,mid,to);
                func(n-1,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)-1) {
            std::cout << res;
            return;
        }
        else {
            printAllSub(str, i + 1, res);//不选str[i]
            printAllSub(str, i + 1, res + str[i]);//选str[i]
        }
    
    }
    
    void main() {
        string test = "abc";
        char  v[4];
        //strncpy_s 优化后更安全的函数
        strncpy_s(v, test.c_str(), test.length() + 1);//必须加1,还占一个位置
        printAllSub(v, 0, " ");
    }

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

    #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 = 1; i < k; i++) {
            if (str[i] == str[k]) {
                flag = 0;
                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;      //1
    
        if (i == n - 1) {
            for (int w = 0; 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 + 1,n);
                    swap(str[i], str[j]);
                }
            }
        }
    }
    
    void main() {
        char str[4] = { 'a','b','c','a'};
        int length = sizeof(str) / sizeof(str[0]);//获取数组长度
        //cout <<"length = " <<length << endl;
        printAllArr(str, 0, length);
    }

    参考资料:

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

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

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

  • 相关阅读:
    解决在cmd命令下不能输入中文方法
    报错注入
    html表单中的name属性和value属性
    xss漏洞
    DVWA-xss反射型(跨站脚本漏洞)
    DVWA-brute force
    owsap top 10 2017(十大web安全应用程序安全)
    sqli_labs less-5
    盲注
    c++ 类
  • 原文地址:https://www.cnblogs.com/paulprayer/p/10000214.html
Copyright © 2011-2022 走看看