动态规划dp

目录

  • 理解dp
  • 一些注意的点:
    • 遍历方式
    • 特殊的边界
    • 状态空间的维度
  • 状态的定义i
  • 最大子数组和

理解dp

动态规划关键:
1.状态定义
2.状态出口
3.状态转移方程:如何缩小问题规模

递归+记忆化

一些注意的点:

遍历方式

要保证状态转移方程右边的状态先于左侧的状态被求出并存储

特殊的边界

状态转移方程可能不适用一些特殊的边界,典型的异常是数组溢出

以下面的题目为例:

给定一个三角形 triangle ,找出自顶向下的最小路径和。

每一步只能移动到下一行中相邻的结点上。相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。也就是说,如果正位于当前行的下标 i ,那么下一步可以移动到下一行的下标 i 或 i + 1 。

示例 1:

输入:triangle = [[2],[3,4],[6,5,7],[4,1,8,3]]
输出:11
解释:如下面简图所示:
2
3 4
6 5 7
4 1 8 3
自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。
示例 2:

输入:triangle = [[-10]]
输出:-10

提示:

1 <= triangle.length <= 200
triangle[0].length == 1
triangle[i].length == triangle[i - 1].length + 1
-104 <= triangle[i][j] <= 104

题解C++

class Solution {
public:
    int minimumTotal(vector<vector<int>>& triangle) {
        int height=triangle.size();
        vector<vector<int>> dp(height);
        //初始化
        if(height>=1){
        dp[0].resize(1);
        dp[0][0]=triangle[0][0];
            if(height>=2){
                dp[1].resize(2);
                dp[1][0]=dp[0][0]+triangle[1][0];
                dp[1][1]=dp[0][0]+triangle[1][1];
            }
        }
        
        for(int i=2;i<height;i++){
            dp[i].resize(i+1);
            dp[i][0]=dp[i-1][0]+triangle[i][0];
            for(int j=1;j<i;j++){
                dp[i][j]=min(dp[i-1][j],dp[i-1][j-1])+triangle[i][j];
            }
            dp[i][i]=dp[i-1][i-1]+triangle[i][i];
        }
        int min_path=INT_MAX;
        for(int i=0;i<height;i++){
            min_path=min(min_path,dp[height-1][i]);
        }
        return min_path;
    }
};

三角形的状态转移方程注意 dp[i][0]和dp[i][i]前者的前驱状态只有dp[i-1][0],后者的前驱状态只有dp[i-1][i-1]

状态空间的维度

状态空间的维度,最常见的是一维和二维,具体需要依据情况而定,主要是看状态转移表达式

状态的定义i

最大子数组和

用 f(i)代表以第 i 个数结尾的「连续子数组的最大和」,我最开始惯性思维老想着前i个数的连续子数组的最大和,就写不出来状态转移方程。因此定义的状态一定要能实现状态间的转移

你可能感兴趣的:(数据结构,动态规划)