美文网首页动态规划
算法思想:递归 回溯 分治 动态规划 2019-04-22

算法思想:递归 回溯 分治 动态规划 2019-04-22

作者: 小爆爆就是我 | 来源:发表于2019-04-27 15:43 被阅读5次

几种算法思想:

一、递归(保留往期第五天任务)

通过LeetCode上【70. 爬楼梯】学习
中文路径:https://leetcode-cn.com/problems/climbing-stairs/submissions/
思路1:将n=1~4的结果列出来,发现规律跟斐波那契数列很像,采用动态规划DP方式, int* sum=new int[n];将上两个情况的和作为新的值返回。代码如下:

class Solution {
    public:
    int climbStairs(int n) {
        if(n==0) return 0;
        if(n==1) return 1;
        
        int* sum=new int[n];
        
        sum[0]=1;
        sum[1]=2;
        for(int i=2;i<n;i++){
            sum[i]=sum[i-1]+sum[i-2];            
        }
        return sum[n-1];            
            
    }
};

思路2:用递归调用方式,调用自身的上两次计算结果 求和返回。
但是无法提交啊不知道为什么运行超时了。执行的时候是ok 的。
https://leetcode-cn.com/problems/climbing-stairs/submissions/

二、回溯
当问题是要求满足某种性质(约束条件)的所有解或最优解时,往往使用回溯法。实现方法有两种:递归和递推(也称迭代)
回溯思路:搜索时采用深度优先算法DFS,一个路径满足要求一搜到底,如果不符合条件则舍弃该子节点所有的路径回到平级的节点去重新搜--不符合就回去即为回溯。

  1. 利用回溯算法求解八皇后问题
    国际象棋8*8棋盘,皇后可以沿横竖斜线走任意步数。
    放置一个皇后占位,下一个皇后需要搜索在某行时是否与占位皇后在同一竖线斜线上,如果到尽头依然在,则回到行进行移动再重复后续步骤。
    中文路径:https://leetcode-cn.com/problems/n-queens/
class Solution {
public:
    vector<vector<string>> solveNQueens(int n) {
 
        if( n <= 0)
            return vector< vector< string> >();
        int col[n];
        vector< vector< string> > res;
        queens( col, 0, n, res);
        return res;
    }
    void queens( int * col, int row, int n, vector< vector< string> > &res){
        if( row == n){
            push(col, res, n);
            return;
        }
        for( int i = 0; i < n; ++i){
            col[row] = i;
            bool flag = true;
            for( int j = 0; j < row; ++j){
                if( col[row] == col[j] || col[row] - col[j] == row - j || col[row] - col[j] == j - row){
                    flag = false;
                    break;
                }
            }
            if(flag)
                queens( col, row+1, n, res);
        }
    }
    //把值压进数组
    void push( int * col, vector< vector< string> > &res, int n){
        vector< string> temp;//这里不能初始化,不然会有初始化值
        for( int i = 0; i < n; ++i){
            string str = "";
            for( int j = 0; j < n; ++j){
                if( col[i] == j)
                    str += 'Q';
                else
                    str += '.';
            }
            temp.push_back(str);
        }
        res.push_back(temp);
    }


};
  1. 利用回溯算法求解 0-1 背包问题

三、分治

  1. 利用分治算法求一组数据的逆序对个数
    求解逆序对个数采用分治法(Divide and Conquer)的一个非常典型的应用--归并排序(MERGE-SORT),是建立在归并操作上的一种有效的排序算法,时间复杂度为O(nlogn)。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路[归并]。

思路:逆序对是指在一个数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。先分后治:一个数组拆称两个对称部分(奇数数组怎么弄我还没研究),对左右数组分别再拆分,拆成一个元素一个数组之后,对最新的左右数组进行比较、排序、记录逆序对、合并成一个数组,如此重复最终和并成一个完整的有序列表。

四、动态规划

  1. 0-1 背包问题
    ※有编号分别为1,2,3,4,5的五件物品,它们的重量分别是2,2,6,5,4,它们的价值分别是6,3,5,4,6,现在给你个承重为10的背包,如何让背包里装入的物品具有最大的价值总和?
    动态规划的核心过程有两部分,一个是找出问题的”子状态”,再一个就是建立“状态转移方程”(所谓的递推公式)动态规划解决问题的核心就在于填表,表填写完毕,最优解也就找到。
    由此可以得出递推关系式:Vi表示第 i 个物品的价值,Wi表示第 i 个物品的体积(重量)

1) j<w(i) V(i,j)=V(i-1,j)

2) j>=w(i) V(i,j)=max{ V(i-1,j),V(i-1,j-w(i))+v(i) }

  1. 最小路径和(详细可看 Minimum Path Sum)
    动态规划的过程可以看做是递归的逆过程,既然递归是从上往下求解,每个大的问题都要先去求解子问题,所以,动态规划是先求解小的子问题,依次往上,所以大问题需要的子问题已经提前求好了。

先生成一张二维dp表,然后按照递归相反的方向求解;
先把dp表的最右下角+最后一行+最后一列的位置填好;
然后找一个普通的位置依赖它下面的位置和左边的位置,所以我们依次从下到上,做右往左,填整张dp表,最后dp[0][0]就是答案。
参考:https://blog.csdn.net/zxzxzx0119/article/details/81227300

class Solution {
public:
    int minPathSum(vector<vector<int>>& grid) {
   
       int mm=grid.size();
        int nn=grid[0].size();
        vector<vector<int> > dis;
        vector<int> vec;
        vec.push_back(grid[0][0]);
        for (int i=1;i<nn;i++){
            vec.push_back(vec[i-1]+grid[0][i]);
        }
        dis.push_back(vec);
        for (int i=1;i<mm;i++){
            vec.clear();
            for (int j=0;j<nn;j++){
                if (j==0)   vec.push_back(dis[i-1][j]+grid[i][j]);
                else    vec.push_back(min(dis[i-1][j],vec[j-1])+grid[i][j]);
            }
            dis.push_back(vec);
        }
        return dis[mm-1][nn-1];


    }
};
  1. 编程实现莱文斯坦最短编辑距离
    word1经过多少步的操作变成(添加、删除、替换)word2:
    https://leetcode-cn.com/problems/edit-distance/submissions/

  2. 编程实现查找两个字符串的最长公共子序列

  3. 编程实现一个数据序列的最长递增子序列

对应的 LeetCode 练习题

实战递归:完成Leetcode上的Letter Combinations of a Phone Number(17)及permutations(46)

(保留往期第六天任务)

实战DP:完成0-1背包问题实现(自我实现)及Leetcode上Palindrome Partitioning II(132)

补充题目:leetcode198 House Robber

(保留往期第七天任务)

Regular Expression Matching(正则表达式匹配)

英文版:https://leetcode.com/problems/regular-expression-matching/

public:
    bool isMatch(string s, string p) {
        int m = s.size(), n = p.size();
          vector<vector<bool>> dp(m + 1, vector<bool>(n + 1, false));
         dp[0][0] = true;
         for (int i = 1; i <= n; ++i) {
              if (p[i - 1] == '.') dp[0][i] = dp[0][i - 1];
          }
         for (int i = 1; i <= m; ++i) {
             for (int j = 1; j <= n; ++j) {
                 if (p[j - 1] == '.') {                     
                     dp[i][j] = dp[i - 1][j] || dp[i][j - 1];
                 } else {
                     dp[i][j] = (s[i - 1] == p[j - 1] || p[j - 1] == '*') && dp[i - 1][j - 1];
                 }
             }
         }
         return dp[m][n];
    }
};

中文版:https://leetcode-cn.com/problems/regular-expression-matching/

Minimum Path Sum(最小路径和)

英文版:https://leetcode.com/problems/minimum-path-sum/

中文版:https://leetcode-cn.com/problems/minimum-path-sum/

见↑四动态规划2
https://leetcode-cn.com/problems/minimum-path-sum/

Coin Change (零钱兑换)

英文版:https://leetcode.com/problems/coin-change/

中文版:https://leetcode-cn.com/problems/coin-change/
递归+memo的DP

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        vector<int> memo(amount + 1, INT_MAX);
        memo[0] = 0;
        return coinChangeDFS(coins, amount, memo);
    }
    int coinChangeDFS(vector<int>& coins, int target, vector<int>& memo) {
        if (target < 0) return - 1;
        if (memo[target] != INT_MAX) return memo[target];
        for (int i = 0; i < coins.size(); ++i) {
            int tmp = coinChangeDFS(coins, target - coins[i], memo);
            if (tmp >= 0) memo[target] = min(memo[target], tmp + 1);
        }
        return memo[target] = (memo[target] == INT_MAX) ? -1 : memo[target];
    }
};

https://www.cnblogs.com/grandyang/p/5138186.html
Best Time to Buy and Sell Stock(买卖股票的最佳时机)

英文版:https://leetcode.com/problems/best-time-to-buy-and-sell-stock/

中文版:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if(prices.size()==0) return 0;
        int profit=0;
        int permin = prices[0];
        for(int i=0;i<prices.size();i++)
        {   
            if(prices[i]<permin) permin = prices[i];//找到买入最小金额
            profit = max(profit, prices[i]-permin); //找到卖出最大差价
            
        }
        return profit;
        
    }
};

Maximum Product Subarray(乘积最大子序列)

英文版:https://leetcode.com/problems/maximum-product-subarray/

中文版:https://leetcode-cn.com/problems/maximum-product-subarray/

class Solution {
public:
    int maxProduct(vector<int>& nums) {
     /*   if(nums.size()==0) return 0;
        if(nums.size()==1) return nums[0];
        int maxval=0;
       //没有通过上传:[-3,3,-8]返回了3,预期是72,我就纳闷了难道-3和8是连续子序列???
        for(int i=0;i<nums.size();i++) {   
            if(maxval<=nums[i]) maxval = nums[i];
            if((i+1)<nums.size()){
                if(maxval<(nums[i]*nums[i+1])) maxval = nums[i]*nums[i+1];//找到大于单元素子序列的双元素子序列    
        }
        else {}
            
        }
        return maxval;
      */
        int max_local = nums[0];
         int min_local = nums[0];
         int max_copy;
         int global = nums[0];
         
         for(int i = 1; i < nums.size(); i++){
              max_copy = max_local;
              max_local = max(max(max_local*nums[i], nums[i]), min_local*nums[i]);
              min_local = min(min(max_copy*nums[i], nums[i]), min_local*nums[i]);
              global = max(global,max_local);
         }
         
         return global;
    
    }
};

Triangle(三角形最小路径和)

英文版:https://leetcode.com/problems/triangle/

中文版:https://leetcode-cn.com/problems/triangle/

class Solution {
public:
    int minimumTotal(vector<vector<int>>& triangle) {
   /* for (int i = 1; i < triangle.size(); ++i) {
            for (int j = 0; j < triangle[i].size(); ++j) {
                if (j == 0) {
                    triangle[i][j] += triangle[i - 1][j];
                } else if (j == triangle[i].size() - 1) {
                    triangle[i][j] += triangle[i - 1][j - 1];
                } else {
                    triangle[i][j] += min(triangle[i - 1][j - 1], triangle[i - 1][j]);
                }
            }
        }
        return *min_element(triangle.back().begin(), triangle.back().end());*/
        vector<int> dp(triangle.back());
        for (int i = (int)triangle.size() - 2; i >= 0; --i) {
            for (int j = 0; j <= i; ++j) {
                dp[j] = min(dp[j], dp[j + 1]) + triangle[i][j];
            }
        }
        return dp[0];
    }
    
};

相关文章

  • 动态规划

    一、分治,回溯,递归,动态规划 1.1、递归的代码模板 1.2、分治(Divide & Conquer)的代码模板...

  • 算法思想:递归 回溯 分治 动态规划 2019-04-22

    几种算法思想: 一、递归(保留往期第五天任务) 通过LeetCode上【70. 爬楼梯】学习中文路径:https:...

  • 高级算法设计与分析

    目录 算法基础 算法复杂性 递归与分治 回溯法与分支限界法 贪心算法 动态规划法 NP问题 概率算法 现代优化算法...

  • 数据结构与算法简述(下)

    目录: 算法简介 排序算法 递归与穷举 贪心与分治 动态规划和回溯 1.算法简介 解题方案的准确而完整的描述,是一...

  • 动态规划问题(Java)

    0. 动态规划分析 0.1 动态规划、递归和贪心算法的区别 动态规划就是利用分治思想和解决冗余的办法来处理问题,所...

  • 一位算法工程师的自我修养

    数据结构与算法 基本算法思想动态规划贪心算法回溯算法分治算法枚举算法 算法基础 时间复杂度 空间复杂度 最大复杂度...

  • 那些经典算法:贪心算法

    贪心算法和分治算法、动态规划算法、回溯算法都是一种编程思想,深入理解这些编程思想,我们也可以根据实际情况设计自己的...

  • 王道程序员求职宝典(九)基本算法及链表

    分治法,动态规划与贪心算法 分治法特征分解解决合并递归:自顶向下 动态规划要素最优子结构重叠子问题递推:自底向上步...

  • 互联网大厂常考算法及套路深度解析

    常考算法 暴力法 回溯法 分支限界法 分治法 动态规划 贪心法 暴力法 也称枚举法、穷举法、蛮力法。 基本思想: ...

  • 12. 动态规划的实现及关键点

    12. 动态规划的实现及关键点 分治+回溯+递归+动态规划 它的本质就是将一个复杂的问题,分解成各种子问题,同时寻...

网友评论

    本文标题:算法思想:递归 回溯 分治 动态规划 2019-04-22

    本文链接:https://www.haomeiwen.com/subject/qgzugqtx.html