zoukankan      html  css  js  c++  java
  • 动态规划

    动态规划

    回顾分治、回溯、递归学习动态规划

    递归模板

    java代码

    public void recur(int level, int param) { 
    
      // terminator 
      if (level > MAX_LEVEL) { 
        // process result 
        return; 
      } 
    
      // process current logic 
      process(level, param); 
    
      // drill down 
      recur( level: level + 1, newParam); 
    
      // restore current status 
     
    }
    

    Python代码

    def recursion(level, param1, param2, ...): 
        # recursion terminator 
        if level > MAX_LEVEL: 
    	   process_result 
    	   return 
    
        # process logic in current level 
        process(level, data...) 
    
        # drill down 
        self.recursion(level + 1, p1, ...) 
    
        # reverse the current level status if needed
    

    分治代码模板

    
    def divide_conquer(problem, param1, param2, ...): 
      # recursion terminator 
      if problem is None: 
    	print_result 
    	return 
    
      # prepare data 
      data = prepare_data(problem) 
      subproblems = split_problem(problem, data) 
    
      # conquer subproblems 
      subresult1 = self.divide_conquer(subproblems[0], p1, ...) 
      subresult2 = self.divide_conquer(subproblems[1], p1, ...) 
      subresult3 = self.divide_conquer(subproblems[2], p1, ...) 
      …
    
      # process and generate the final result 
      result = process_result(subresult1, subresult2, subresult3, …)
    	
      # revert the current level states
    

    感触

    1. 人肉递归低效、很累
    2. 找到最近最简方法,将其拆解成可重复解决的问题(寻找重复子问题)
    3. 数学归纳法思维(抵制人肉递归的诱惑)

    本质:寻找重复性——>计算机指令集

    动态规划 Dynamic programming

    wiki定义

    Divide & Conquer + Optimal substructure

    分治+最优子结构

    关键点

    1. 动态规划和递归或者分治没有根本上的区别(关键看有无最优的子结构)
    2. 共性:找到重复子问题
    3. 差异性:最优子结构,中途可以淘汰次优解

    实战一:斐波那契数列

    int fib (int n) 
    {  
        if (n <= 0) 
        {   
            return 0;  
        } 
        else if (n == 1) 
        {
            return 1;  
        } 
        else 
        {
            return fib (n - 1) + fib (n - 2);  
        } 
    } 
    
    
    int fib(int n)
    {
        return n<=1?n:fib(n-1)+fib(n-2);
    }
    

    实战例题二:路径计数

    不同路径I

    class Solution {
    public:
        int uniquePaths(int m, int n) {
            int dp[m][n];
            for (int i = 0; i < m; i++)
            {
                dp[i][0]=1;
            }
            for (int j = 0; j < n; j++)
            {
                dp[0][j]=1;
            }
            for (int i = 1; i < m; i++)
            {
                for (int j = 1; j < n; j++)
                {
                    dp[i][j]=dp[i-1][j]+dp[i][j-1];
                }
            }
            return dp[m-1][n-1];
        }
    };
    

    不同路径 II

    class Solution {
    public:
        int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
            int m=obstacleGrid.size();
            int n=obstacleGrid[0].size();
            vector<vector<long int>>dp(m,vector<long int>(n,0));
            dp[0][0]=obstacleGrid[0][0]==1?0:1;
            for (int i = 0; i < m; i++)
            {
              for(int j=0;j<n;j++)
                {
                    if(i==0&&j!=0) //第一行的时候
                    {
                        if(obstacleGrid[i][j]!=1)
                        {
                            dp[i][j]=dp[i][j-1];
                        }
                        else
                        {
                            dp[i][j]=0;
                        }
                    }
                   else if(j==0&&i!=0)//第一列的时候
                    {
                        if(obstacleGrid[i][j]!=1)
                        {
                            dp[i][j]=dp[i-1][j];
                        }
                        else
                        {
                            dp[i][j]=0;
                        }
                    }
                    else if(i!=0&&j!=0) //表示第一行,第一列的时候
                    {
                        if(obstacleGrid[i][j]==1)
                        {
                            dp[i][j]=0;
                        }
                        else
                        {
                            dp[i][j]=dp[i-1][j]+dp[i][j-1];
                        }
                    }
                }
            }
              return dp[m-1][n-1];
        }
    };
    

    动态规划关键点

    1. 最优子结构 opt[n]=best_of(opt[n-1],opt[n-2],……)
    2. 存储中间状态:opt[i]
    3. 递推公式(状态转移方程)

    实战例题三:最长公共子序列

    最长公共子序列

    class Solution {
    public:
        int longestCommonSubsequence(string text1, string text2) {
            const int rows = text1.size();
            const int cols = text2.size();
            vector<vector<int>> dp(rows, vector<int>(cols, 0));
            // 求解边界 BEGIN
            if (text1[0] == text2[0])
                dp[0][0] = 1;
            for (int i = 1; i < rows; i++) {
                dp[i][0] = dp[i - 1][0];
                if (text1[i] == text2[0]) {
                    dp[i][0] = 1;
                }
            }
            for (int j = 1; j < cols; j++) {
                dp[0][j] = dp[0][j - 1];
                if (text1[0] == text2[j]) {
                    dp[0][j] = 1;
                }
            }
            // 求解边界 END
    
            for (int i = 1; i < rows; i++)
                for (int j = 1; j < cols; j++) {
                    if (text1[i] == text2[j]) { // 遇到匹配字符
                        dp[i][j] = dp[i - 1][j - 1] + 1;
                    } else { // 不匹配时,继承左上中的较大值
                        dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
                    }
                }
            return dp[rows - 1][cols - 1];
        }
    };
    
    

    小结

    1. 打破自己的思维惯性,形成机器思维
    2. 理解复杂逻辑的关键
    3. 也是职业进阶的要领根本

    拓展

    B 站搜索: mit 动态规划

  • 相关阅读:
    leetcode回溯总结
    排序算法详解
    Java核心基础知识泛型
    leetcode贪心算法
    Linux虚拟机配置及安装Redis
    HJ6质数因子
    冒泡排序(升序)
    动态规划 背包问题
    配置mycatschema.xml
    HJ106字符逆序
  • 原文地址:https://www.cnblogs.com/liugangjiayou/p/12492896.html
Copyright © 2011-2022 走看看