动态规划

动态规划

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

递归模板

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 动态规划

原文地址:https://www.cnblogs.com/liugangjiayou/p/12492896.html