Leetcode 动态规划 (持续更新)

【LeetCode】动态规划类题目详解 一文学会动态规划算法 阅读详情

动态规划

  • 解决问题
    1. Max / Min
    2. Yes / No
    3. Count(*)
    4. Can’t sort / swap
  • 四要素
    1. 状态 state
    2. 初始化 init
    3. 方程 function
    4. 结果 result
  • 常见类型
    1. 矩阵DP
    2. 序列DP
    3. 双序列DP
    4. 背包问题

题 62. 不同路径

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
问总共有多少条不同的路径?
这里写图片描述
例如,上图是一个7 x 3 的网格。有多少可能的路径?
说明:m 和 n 的值均不超过 100。
输入: m = 3, n = 2
输出: 3

My Answer:

class Solution {
    public int uniquePaths(int m, int n) {
        //state
        int[][] f = new int[n][m];
        //init
        for(int i = 0; i < n; i++) f[i][0] = 1;
        for(int i = 0; i < m; i++) f[0][i] = 1;
        //function
        for(int i = 1; i < n; i++){
            for(int j = 1; j < m; j++){
                f[i][j] = f[i-1][j] + f[i][j-1];
            }
        }
        //result
        return f[n-1][m-1];
    }
}

题 63. 不同路径 II

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
这里写图片描述
网格中的障碍物和空位置分别用 1 和 0 来表示。
输入:

[
  [0,0,0],
  [0,1,0],
  [0,0,0]
]

输出: 2

My Answer:

class Solution {
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        if(obstacleGrid[0][0] == 1){
            return 0;
        }

        int n = obstacleGrid.length;
        int m = obstacleGrid[0].length;

        //state f 表示从[0][0]走到[i][j]有多少种不同的走法
        int [][] f = new int[n][m];

        //init
        f[0][0] = 1;

        int init_n = 1;
        int init_m = 1;

        for(int i = 1; i < n; i++){
            if(obstacleGrid[i][0] == 1) init_n = 0;
            f[i][0] = init_n;
        }
        for(int i = 1; i < m; i++){
            if(obstacleGrid[0][i] == 1) init_m = 0;
            f[0][i] = init_m;
        }

        //function
        if(n > 1 && m >1){    
            for(int i = 1; i < n; i++){
                for(int j = 1; j < m; j++){
                    if(obstacleGrid[i][j] == 1){
                        f[i][j] = 0;
                    }else{
                        f[i][j] = f[i-1][j] + f[i][j-1];   
                    }
                }
            }
        }

        //result
        return f[n-1][m-1];

    }
}

题 53. 最大子序和

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:

输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

My Answer:

class Solution {
/* public int[][] matrix(int[] nums){
        // DP 预处理解法  超时
        int n = nums.length;
        int[][] A = new int[n][n];
        for(int i = 0; i < n; i++){
            A[i][i] = nums[i];
        }
        for(int i = 0; i < n; i++){
            for(int j = 0; j < i; j++){
                A[i-j-1][i] = A[i-j][i] + nums[i-j-1];
            }
        }
        return A;
    }
    public int maxSubArray(int[] nums) {
        //state f[i]表示以前i项以i结尾的最大子数组和
        int n = nums.length;
        int [] f = new int[n];
        int [][] sums = matrix(nums);
        //init & function
        for(int i = 0; i < n; i++){
            f[i] = nums[i];
            for(int j = 0; j < i; j++){
                f[i] = Math.max(f[i],nums[j] + sums[j+1][i]);
            }
        }
        //result
        int result = f[0];
        for(int i = 0; i < n; i++){
            result = Math.max(result,f[i]);
        }
        return result;
    } */

    public int maxSubArray(int[] nums) {
        int n = nums.length;
        if(n == 1) return nums[0];
        int sum = nums[0];
        int maxsum = nums[0];
        for(int i = 1; i < n; i++){
            sum = Math.max(nums[i],sum + nums[i]);
            maxsum = Math.max(maxsum,sum);
        }
        return maxsum;
    }
}

题 64. 最小路径和

给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。
输入:

[
  [1,3,1],
  [1,5,1],
  [4,2,1]
]

输出: 7
解释: 因为路径 1→3→1→1→1 的总和最小。

My Answer:

class Solution {
    public int minPathSum(int[][] grid) {
        //state
        int n = grid.length;
        int m= grid[0].length;
        int [][] f = new int[n][m];

        //init
        f[0][0] = grid[0][0];
        for(int i = 1; i < n; i++){
            f[i][0] = f[i-1][0] + grid[i][0];
        }
        for(int j = 1; j < m; j++){
            f[0][j] = f[0][j-1] + grid[0][j];
        }

        //function
        for(int i = 1; i < n; i++){
            for(int j = 1; j < m; j++){
                f[i][j] = Math.min(f[i-1][j],f[i][j-1]) + grid[i][j];
            }
        }
        //result
        return f[n-1][m-1];
    }
}

题 70. 爬楼梯

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。

1 阶 + 1 阶
2 阶

My Answer:

class Solution {
    public int climbStairs(int n) {
        //state 表示 爬到第i个台阶有多少种方案
        int[] f = new int[n + 1];
        if (n == 0) return 0;
        if (n == 1) return 1;
        if (n == 2) return 2;
        //init
        f[0] = 0; f[1] = 1; f[2] = 2;
        //function
        if(n > 2){
            for(int i = 3; i <= n; i++){
                f[i] = f[i-1] + f[i-2];
            }
        }
        //result
        return f[n];
    }
}

题 72. 编辑距离

给定两个单词 word1 和 word2,计算出将 word1 转换成 word2 所使用的最少操作数 。
你可以对一个单词进行如下三种操作:

  1. 插入一个字符
  2. 删除一个字符
  3. 替换一个字符
    输入: word1 = "horse", word2 = "ros"
    输出: 3
    解释:
horse -> rorse (将 'h' 替换为 'r')
rorse -> rose (删除 'r')
rose -> ros (删除 'e')

My Answer:

class Solution {
    public int minDistance(String word1, String word2) {
        //state f[i][j] 表示 word1 的前 i 个字符配上 word2 的前 j 个字符最少需要编辑几次使他们相等
        int n = word1.length();
        int m = word2.length();
        if(n == 0) return m;
        if(m == 0) return n;
        int [][] f = new int[n+1][m+1];
        //init
        for(int i = 0; i <= n; i++) f[i][0] = i;
        for(int i = 0; i <= m; i++) f[0][i] = i;
        //function
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= m; j++){
                if(word1.charAt(i-1) == word2.charAt(j-1)){
                    f[i][j] = Math.min(f[i-1][j-1],f[i-1][j] + 1);
                    f[i][j] = Math.min(f[i][j],f[i][j-1] + 1);
                }else{
                    f[i][j] = Math.min(f[i][j-1],f[i-1][j]);
                    f[i][j] = Math.min(f[i][j],f[i-1][j-1]) + 1;
                }
            }
        }
        //result
        return f[n][m];
    }
}

题 97. 交错字符串

给定三个字符串 s1, s2, s3, 验证 s3 是否是由 s1 和 s2 交错组成的。
示例 1:

输入: s1 = "aabcc", s2 = "dbbca", s3 = "aadbbcbcac"
输出: true

示例 2:

输入: s1 = "aabcc", s2 = "dbbca", s3 = "aadbbbaccc"
输出: false

My Answer:

class Solution {
    public boolean isInterleave(String s1, String s2, String s3) {
        //state f[i][j] 表示第一个字符串前i个字符和第二个字符串前j个字符是否能够组成第3个字符串的前i+j个字符
        int n = s1.length();
        int m = s2.length();
        int p = s3.length();
        if(n + m != p) return false;
        if(n == 0 && m ==0) return true;
        if(m == 0) return s1.equals(s3);
        if(n == 0) return s2.equals(s3);
        boolean [][] f = new boolean[n+1][m+1];
        //init
        f[0][0] = true;
        for(int i = 1; i <= n; i++){
            f[i][0] = s1.charAt(i-1) == s3.charAt(i-1) && f[i-1][0];   
        }
        for(int i = 1; i <= m; i++){
            f[0][i] = s2.charAt(i-1) == s3.charAt(i-1) && f[0][i-1];   
        }
        //function
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= m; j++){
                f[i][j] = (f[i][j-1] && s2.charAt(j-1) == s3.charAt(i+j-1)) || ((f[i-1][j] && s1.charAt(i-1) == s3.charAt(i+j-1)));
            }
        }
        //result
        return f[n][m];

    }
}

题 115. 不同的子序列

给定一个字符串 S 和一个字符串 T,计算在 S 的子序列中 T 出现的个数。
一个字符串的一个子序列是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,”ACE” 是 “ABCDE” 的一个子序列,而 “AEC” 不是)
示例 1:

输入: S = "rabbbit", T = "rabbit"
输出: 3
解释:

如下图所示, 有 3 种可以从 S 中得到 “rabbit” 的方案。
(上箭头符号 ^ 表示选取的字母)

rabbbit
^^^^ ^^
rabbbit
^^ ^^^^
rabbbit
^^^ ^^^

示例 2:

输入: S = "babgbag", T = "bag"
输出: 5
解释:

如下图所示, 有 5 种可以从 S 中得到 “bag” 的方案。
(上箭头符号 ^ 表示选取的字母)

babgbag
^^ ^
babgbag
^^    ^
babgbag
^    ^^
babgbag
  ^  ^^
babgbag
    ^^^

My Answer:

class Solution {
    public int numDistinct(String s, String t) {
        //state
        int n = s.length();
        int m = t.length();
        if(n == 0) return 0;
        if(m == 0) return 1; 
        int [][] f = new int[n+1][m+1];
        //init
        for(int i = 0; i <= m; i++) f[0][i] = 0; 
        for(int i = 0; i <= n; i++) f[i][0] = 1; 
        //function
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= m; j++){
                if(s.charAt(i-1) == t.charAt(j-1)){
                    f[i][j] = f[i-1][j] + f[i-1][j-1];
                } else {
                    f[i][j] = f[i-1][j];
                }
            }
        }
        //result
        return f[n][m];
    }
}

题 120. 三角形最小路径和

给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。

例如,给定三角形:

[
     [2],
    [3,4],
   [6,5,7],
  [4,1,8,3]
]

自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。

My Answer:

class Solution {
    public int minimumTotal(List<List<Integer>> triangle) {
        if (triangle == null || triangle.size() == 0) return -1; 
        if (triangle.get(0) == null || triangle.get(0).size() == 0) return -1; 
        int n = triangle.size();
        int [][] f = new int[n][n];
        //init
        f[0][0] = triangle.get(0).get(0);
        for(int i = 1; i < n; i++){
            f[i][0] = f[i - 1][0] + triangle.get(i).get(0);
            f[i][i] = f[i - 1][i - 1] + triangle.get(i).get(i);
        } 
        //function
        for(int i = 1; i < n; i++){
            for(int j = 1; j < i; j++){
                f[i][j] = Math.min(f[i - 1][j], f[i - 1][j - 1]) + triangle.get(i).get(j);
            }
        } 
        //result
        int best = f[n - 1][0];
        for (int i = 1; i < n; i++) {
            best = Math.min(best, f[n - 1][i]);
        }
        return best;
    }
}

题 121. 买卖股票的最佳时机

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。

注意你不能在买入股票前卖出股票。

示例 1:

输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
     注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。

示例 2:

输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

My Answer:

class Solution {
    public int maxProfit(int[] prices) {
        //state 前i天以第i天价格售出的最大利润
        int n = prices.length;
        int [] f = new int [n];
        //init function
        for(int i = 0; i < n; i++){
            f[i] = 0;
            for(int j = 0; i > j; j++){
                if(prices[j] < prices[i]){
                    f[i] = f[i] > prices[i] - prices[j] ? f[i] : prices[i] - prices[j];
                }
            }
        }
        //result
        int result = 0;
        for(int i = 0; i < n; i++){
            result = Math.max(result,f[i]);
        }
        return result;
    }
}

题 132. 分割回文串 II

给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。

返回符合要求的最少分割次数。

示例:

输入: "aab"
输出: 1
解释: 进行一次分割就可将 s 分割成 ["aa","b"] 这样两个回文子串。

My Answer:

class Solution {
    public int minCut(String s) {
        //state 前i个字符最少分割次数
        int n = s.length();
        int[] f =  new int[n + 1];
        //init
        f[0] = -1;
        //function
        boolean [][] check = getCheck(s,n);

        for(int i = 1; i <= n; i++){
            f[i] = f[i - 1] + 1;
            for(int j = 0; j < i; j++){
                if(check[j][i-1]){
                    f[i] = Math.min(f[i],f[j]+1);   
                }
            }
        }
        //result
        return f[n];
    }
    public boolean[][] getCheck (String s,int n){
        boolean[][] check = new boolean[n][n];
        int i, j, p;
        for (i = 0; i < n; ++i) {
            for (j = 0; j < n; ++j) {
                check[i][j] = false;
            }
        }
        for (p = 0; p < n; ++p) {
            i = j = p;
            while (i >= 0 && j < n && s.charAt(i) == s.charAt(j)) {
                check[i][j] = true;
                --i;
                ++j;
            }
        }

        for (p = 0; p < n-1; ++p) {
            i = p;
            j = p + 1;
            while (i >= 0 && j < n && s.charAt(i) == s.charAt(j)) {
                check[i][j] = true;
                --i;
                ++j;
            }
        }
        return check;
    }
}

题 139. 单词拆分

给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。

说明:

  • 拆分时可以重复使用字典中的单词。
  • 你可以假设字典中没有重复的单词。

示例 1:

输入: s = "leetcode", wordDict = ["leet", "code"]
输出: true
解释: 返回 true 因为 "leetcode" 可以被拆分成 "leet code"。

示例 2:

输入: s = "applepenapple", wordDict = ["apple", "pen"]
输出: true
解释: 返回 true 因为 "applepenapple" 可以被拆分成 "apple pen apple"。
注意你可以重复使用字典中的单词。

示例 3:

输入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
输出: false

My Answer:

class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        if (s == null || s.length() == 0) {
            return true;
        }
        //state 前i个字符是否能被完美拆分
        int maxLength = getMaxLength(wordDict);
        int n = s.length();
        boolean[] f = new boolean[n + 1];
        //init
        f[0] = true;

        //function
        for(int i = 1; i <= n; i++){
            f[i] = false;
            for(int j = 1; j <= maxLength && i >= j; j++){
                if(f[i-j] && wordDict.contains(s.substring(i-j,i))){
                    f[i] = true;
                    break;
                }
            }
        }
        //result
        return f[n];
    }
    private int getMaxLength(List<String> dict) {
        int maxLength = 0;
        for (String word : dict) {
            maxLength = Math.max(maxLength, word.length());
        }
        return maxLength;
    }
}

题 198. 打家劫舍

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。

示例 1:

输入: [1,2,3,1]
输出: 4

示例 2:

输入: [2,7,9,3,1]
输出: 12

My Answer

class Solution {
    public int rob(int[] nums) {
        //state f[i] 表示前i个房间最高金额
        int n = nums.length;
        int [] f = new int[n+1];
        //init
        if(n == 0) return 0;
        f[0] = 0;
        f[1] = nums[0];
        //function
        for(int i = 2; i <= n; i++){
            f[i] = Math.max(f[i-1],f[i-2] + nums[i-1]);
        }
        //result
        return f[n];
    }
}

题 300. 最长上升子序列

给定一个无序的整数数组,找到其中最长上升子序列的长度。

示例:

输入: [10,9,2,5,3,7,101,18]
输出: 4 
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
说明:
可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
你算法的时间复杂度应该为 O(n2) 。

My Answer:

class Solution {
    public int lengthOfLIS(int[] nums) {
        //state f[i] 表示前i个数中以第i个结尾的LIS的长度
        int n = nums.length;
        int [] f = new int[n];
        //init & function
        for(int i = 0; i < n; i++){
            f[i] = 1;
            for(int j = 0; j < i; j++){
                if(nums[j] < nums[i])
                    f[i] = f[i] > f[j] + 1 ? f[i] : f[j] + 1;
            }

        }
        //result
        int result = 0;
        for(int i = 0; i < n; i++){
            result = Math.max(result,f[i]);
        }
        return result;
    }
}

题 303. 区域和检索 - 数组不可变

给定一个整数数组 nums,求出数组从索引 i 到 j (i ≤ j) 范围内元素的总和,包含 i, j 两点。

示例:

给定 nums = [-2, 0, 3, -5, 2, -1],求和函数为 sumRange()

sumRange(0, 2) -> 1
sumRange(2, 5) -> -1
sumRange(0, 5) -> -3

说明:

  • 你可以假设数组不可变。
  • 会多次调用 sumRange 方法。

My Answer:

//有趣的想法
class NumArray {
    private:
    vector<int> sum;
    int n;
public:
    NumArray(vector<int> const &nums) {
        n = nums.size();
        sum.resize(n + 1);
        for (int i = 0; i < n; ++i) {
            sum[i + 1] = sum[i] + nums[i];
        }
    }

    int sumRange(int i, int j) {
        return sum[j + 1] - sum[i];
    }

    ~NumArray() {
        sum.clear();
    }
};

/**
 * Your NumArray object will be instantiated and called as such:
 * NumArray obj = new NumArray(nums);
 * int param_1 = obj.sumRange(i,j);
 */

题 338. 比特位计数

给定一个非负整数 num。对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回。

示例 1:

输入: 2
输出: [0,1,1]

示例 2:

输入: 5
输出: [0,1,1,2,1,2]

My Answer:

class Solution {

    public int NumberOf1(int n) {
        int count = 0;
        while(n! = 0){
            count++;
            n = n & (n - 1);//每次都消除该数中最右边的1
         }
        return count;
    }

    public int[] countBits(int num) {
        int [] res = new int[num + 1];
        for(int i = 0; i <= num; i++){
            res[i] = NumberOf1(i);
        }
        return res;
    }
}

题 413. 等差数列划分

如果一个数列至少有三个元素,并且任意两个相邻元素之差相同,则称该数列为等差数列。

例如,以下数列为等差数列:

1, 3, 5, 7, 9
7, 7, 7, 7
3, -1, -5, -9

以下数列不是等差数列。

1, 1, 2, 5, 7

数组 A 包含 N 个数,且索引从0开始。数组 A 的一个子数组划分为数组 (P, Q),
P 与 Q 是整数且满足 0 <= P < Q < N 。
如果满足以下条件,则称子数组(P, Q)为等差数组:
元素 A[P], A[p + 1], ..., A[Q - 1], A[Q] 是等差的。并且 P + 1 < Q 。

函数要返回数组 A 中所有为等差数组的子数组个数。

示例:

A = [1, 2, 3, 4]

返回: 3, A 中有三个子等差数组: [1, 2, 3], [2, 3, 4] 以及自身 [1, 2, 3, 4]。

My Answer:

class Solution {
    public int numberOfArithmeticSlices(int[] A) {
     //state f[i] 表示前i个数以A[i]结尾的等差数列数量
        int n = A.length;
        int[] f = new int[n];
        //init
        if(n < 3) return 0; 
        //function
        for(int i = 0; i < n; i++){
            f[i] = 0;
            if(i < 2) continue; 
            if(A[i] - A[i-1] == A[i-1] - A[i-2]){
                f[i] = f[i-1] + 1;
            }
        }
        //result
        int result = 0;
        for(int i = 0; i < n; i++){
            result += f[i];
        }
        return result;
    }
}

题 494. 目标和

给定一个非负整数数组,a1, a2, …, an, 和一个目标数,S。现在你有两个符号 + 和 -。对于数组中的任意一个整数,你都可以从 + 或 -中选择一个符号添加在前面。

返回可以使最终数组和为目标数 S 的所有添加符号的方法数。

示例 1:

输入: nums: [1, 1, 1, 1, 1], S: 3
输出: 5
解释: 
-1+1+1+1+1 = 3
+1-1+1+1+1 = 3
+1+1-1+1+1 = 3
+1+1+1-1+1 = 3
+1+1+1+1-1 = 3

一共有5种方法让最终目标和为3。

注意:

  • 数组的长度不会超过20,并且数组中的值全为正数。
  • 初始的数组的和不会超过1000。
  • 保证返回的最终结果为32位整数。

My Answer:

class Solution {

    public int findTargetSumWays(int[] nums, int S) {
        int n = nums.length;
        int sum = 0;
        for (int i = 0; i < nums.length; i++) {
            sum += nums[i];
        }
        if (S > sum || (sum + S) % 2 == 1)
            return 0;
        return subsetSum(nums, (sum + S) / 2);
    }

    private int subsetSum(int[] nums, int S) {
        int[] dp = new int[S + 1];
        dp[0] = 1;//C(0,0)=1
        for (int i = 0; i < nums.length; i++) {
            for (int j = S; j >= nums[i]; j--) {
                dp[j] += dp[j - nums[i]];
            }
        }
        return dp[S];
    }
}

题 746. 使用最小花费爬楼梯

数组的每个索引做为一个阶梯,第 i 个阶梯对应着一个非负数的体力花费值 cost[i] (索引从0开始)。

每当你爬上一个阶梯你都要花费对应的体力花费值,然后你可以选择继续爬一个阶梯或者爬两个阶梯。

您需要找到达到楼层顶部的最低花费。在开始时,你可以选择从索引为 0 或 1 的元素作为初始阶梯。

示例 1:

输入: cost = [10, 15, 20]
输出: 15
解释: 最低花费是从cost[1]开始,然后走两步即可到阶梯顶,一共花费15。

示例 2:

输入: cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1]
输出: 6
解释: 最低花费方式是从cost[0]开始,逐个经过那些1,跳过cost[3],一共花费6。

注意:

  • cost 的长度将会在 [2, 1000]。
  • 每一个 cost[i] 将会是一个Integer类型,范围为 [0, 999]。

My Answer:

class Solution {
    public int minCostClimbingStairs(int[] cost) {
        //state f[i] 表示从走到阶梯i最小的花费
        int[] f = new int[cost.length];
        if(cost.length == 1){
            return cost[0];
        }
        if(cost.length == 2){
            return cost[1];
        }
        //init
        f[0] = cost[0];
        f[1] = cost[1];
        //function
        if(cost.length > 2){
            for(int i = 2; i < cost.length; i++){
                f[i] = Math.min(f[i-1],f[i-2]) + cost[i];
            }
        }
        //result
        return Math.min(f[cost.length - 1],f[cost.length - 2]);
    }
}
【LeetCode】动态规划系列总结 动态规划系列总结用到dp的题目类型解题套路322. 零钱兑换 参考:labuladong的算法小抄 用到dp的题目类型 动态规划问题的一般形式都是求最值 解题套路 动态规划三要素: 重叠子问题 最优子结构 状态转移方程 核心思想就是穷举所有可行解求最值—>列出正确的「状态转移方程」 得到状态转移方程: 状态转移方程其实就代表着暴力解,然后用DP table 优化 明确 base case 明确「状态」 原问题和子问题中会变化的变量 明确「选择」 导致状态变化的行为 定义 dp 数组/函数的含义 阅读详情

相关推荐

【Leetcode】动态规划问题详解(持续更新)

新增加leetcode上适合用动态规划求解的三道题目:Minimum Path Sum、Unique Paths、Unique Paths II。

wepon的专栏 1万+

leetcode之动态规划

以爬楼梯问题为例,总结动态规划的步骤:第一步:定义dp数组(可能时一维数组,也可能是二维数组),这一步需要确定两个问题:(1)dp[i]或者dp[i][j]是什么含义(比如在爬楼梯问题中,dp[i]表示到达第i层有多少种爬法)(2)到底开多大的空间,你搞懂了第一个问题dp[i]或者dp[i][j]是什么含义,就知道到底要开多大的空间了,由于在爬楼梯问题中,dp[i]表示到达第i层有多少种爬法,你最后求的是到达第n层所需要的爬法数,所以最后要返回的是dp[n],所以显然要开n+1大小的数组,这样才能有dp[n

weixin_47414034的博客 2289

leetcode 专题:动态规划 python 版(持续更新中)

递归:自己调用自己。代码比较简洁,但是浪费空间,有许多重复计算。 迭代:利用已知的变量值,根据递推公式不断得到新的值,一直到解决问题为止。代码相对复杂一点。 递归中一定有迭代,但是迭代中不一定有递归,大部分可以相互转换。能用迭代的不用递归,递归调用函数,浪费空间,并且递归太深容易造成堆栈的溢出。 动态规划:通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。通常许多子问题非常相似,...

Yenpo Ma的博客 1935

LeetCode刷题笔记之动态规划(一)

LeetCode刷题笔记之动态规划(一)

abtgu的博客 792

DeepSeek Harness 实战:构建可编排的 Agent 插件与工作流

在大模型应用开发中,AI Agent 的落地常面临逻辑耦合、工具接入繁琐、流程难以编排等工程化痛点。DeepSeek Harness 作为一种面向 Agent 运行时的开发层,将模型调用、插件扩展、工作流编排与执行状态统一收敛,为开发者提供可维护、可观测的控制框架。它并非替代 LangChain 或 Dify,而是更聚焦于模型为核心的应用组装范式:插件承载可复用的能力单元,工作流定义节点间的条件跳转与数据传递,从而让复杂业务既能可视化设计,又能通过代码深度定制。从客服知识库到 PRD 评审助手,这类模式适用

weixin_33805743的博客 336

Linux DRM驱动实战:手把手教你用drmModeSetCrtc显示纯色画面(附完整代码)

本文提供了一份完整的Linux DRM驱动实战指南,手把手教你使用drmModeSetCrtc在屏幕上显示纯色画面。文章详细解析了从打开设备、获取资源、创建帧缓冲区到最终提交画面的完整调用流程,并附带了可直接编译运行的完整代码示例,帮助开发者快速上手Linux图形底层开发。

weixin_29216353的博客 514

leetcode-动态规划-python

一、动态规划 动态规划的三要素:最优子结构,边界和状态转移函数, 最优子结构是指每个阶段的最优状态可以从之前某个阶段的某个或某些状态直接得到(子问题的最优解能够决定这个问题的最优解) 边界指的是问题最小子集的解(初始范围) 状态转移函数是指从一个阶段向另一个阶段过度的具体形式,描述的是两个相邻子问题之间的关系(递推式) 重叠子问题,对每个子问题只计算一次,然后将其计算的结果保存到一个表格中,每一次需要上一个子问题解时,进行调用,只要o(1)时间复杂度,准确的说,动态规划是利用空间去换取时间的算法. 判断

Greeksilverfir的博客 705

Leetcode动态规划系列题解合集(持续更新)

Leetcode动态规划系列题解合集(持续更新)

这里是Mae。 1732

小样本物体检测

Few-Shot Object Detection with Attention-RPN and Multi-Relation Detector 关键:提出了一个小样本数据集:数据集 步骤: 提取support img为一过滤特征 使用support img的特征来过滤query img,最后将过滤后的特征输入RPN网络中 RPN网络层的输出,会进行多头判断,使用全局信息检测,使用局部信息检测,一对多检测 Attention-Based Region Proposal Network Increme

qq_21157073的博客 5266

【零基础】手把手教你使用Docker部署Springboot项目(详细版)

还不会使用Docker部署Springboot项目的小伙伴,不妨进来看看呗,希望文中内容对你有所帮助!

fengprogrammer的博客 2万+

LeetCode动态规划题目总结(持续更新中)

Sell Stock 121. Best Time to Buy and Sell Stock 题目:https://leetcode.com/problems/best-time-to-buy-and-sell-stock/ 思路: 卖出能得到最高profit是在之前的几天中最低价的那天买入, ...

pyxiea 897

15、远程操作水下航行器(ROV)的非线性控制系统设计

本文探讨了远程操作水下航行器(ROV)的非线性控制系统设计,重点介绍了多变量PID控制、改进的PD控制和滑模控制三种方法。详细阐述了各控制策略的原理、建模与仿真过程,并通过对比分析其在稳态误差、振荡、抖振及跟踪效果等方面的性能表现。文章还提出了针对不同控制方法的优化建议,并给出了选择合适控制策略的决策流程,为ROV高精度运动控制提供了系统性解决方案。

sun99的博客 158

LeetCode刷题笔记之动态规划(持续更新)

本篇博客记录本人在LeetCode上练习动态规划算法的记录,分为简单、中等、困难三个等级,每次刷题记录也都会附上时间,方便阅览。

weixin_51624761的博客 645

别再盲目学AI了,先搞懂这5种最赚钱的变现模式

2026年,“手搓”成为一个热词——用AI工具独立完成从想法到产品的全过程。央视《焦点访谈》专门报道了这一现象。云南小伙刘梓瑜用一台电脑、几款AI工具、3000元成本和10天时间,创作了AI短片,48小时播放量破千万。华为在HDC 2026上发布的,是一个“懂鸿蒙的编程智能体”,面向独立开发者一站式开箱即用。它内置UI意图验证Agent,能在模拟器上自动运行应用、判断UI和交互逻辑是否正确。故障修复成功率超过80%,分钟级自主修复。

ZDQ58818的博客 311

cdk&java | 分子sdf文件生成

sdf

ASKCOS博客 1091

leetcode动态规划(持续更新)

5. 最长回文子串 思路分析: 先看一个成功的例子:abba,A[1]和A[4]匹配,A[2]和A[3]匹配,更符合实际来看,当我们不看A[4]时,abb,最长回文串为bb,当我们更新到abba时候,最长回文串更新到abba,所以整个的循环不变量是相应的最长回文串 j i a b b a dp: 初始化: 0 0 0 0 0 0 0 0 0 0 0 0 ...

yan-xiao-liu的博客 213

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

C++力扣刷题记录——动态规划(持续更新)

动态规划思路 确定DP状态 最优子结构:可以分成子结构求 无后效:只和前面一个有关 确定转移方程 根据分类讨论确定 写程序 根据分类讨论确定 3写程序

m0_37668446的博客 548

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

【LeetCode动态规划标签入门总结】 线性DP(持续更新)

动态规划 动态规划(Dynamic Programming),简称DP,为了求最优解,我们可以将问题分成多个子问题,先求子问题的最优解,从而递推出全局最优解,动态规划的思想基于分治。 动态规划一般要考虑状态表示和状态方程。考虑到有些DP问题时间复杂度较大,一般用迭代,也可以用递归来写。 线性DP 线性DP最大的特征是从一个最小的子问题来向大的问题推导的。所以叫线性DP,例如斐波那契数列 53. 最大子序和 在该问题上,最小的子问题是序列只有一个数字,那么子序列和最大值肯定是该数字本身。 如果有两个数字,要

Miiiiiiiiiii的博客 515

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

日常leetcode代码(动态规划)思路总结(持续更新)

leetcode题目思想总结

qq_23301117的博客 1647

LeetCode动态规划题总结【持续更新】

以下题号均为LeetCode题号,便于查看原题。 10.Regular Expression Matching 题意:实现字符串的正则匹配,包含'.' 和 '*'。'.' 匹配任意一个字符,"*" 匹配 '*' 之前的0个或多个字符。 example: isMatch("aa","a") → false isMatch("aa","aa") → true isMatch("a...

weixin_30795127的博客 146

LeetCode(动态规划模块 20题) 持续更新

第一题 这里求数字 n 的二进制数中1的个数的方法巧妙。 class Solution { public int[] countBits(int num) { int[] res = new int[num+1]; for(int i = 0 ; i <= num ; i++) { res[i] = popcount(i); } return res; } public i

qq_45260619的博客 344

LeetCode刷题之动态规划(持续更新)

1.爬楼梯 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? 注意:给定 n 是一个正整数。 示例 输入: 2 输出: 2 解释: 有两种方法可以爬到楼顶。 1. 1 阶 + 1 阶 2. 2 阶 public class Test { /** * 动态规划 * 思路:到达n层,有两种方...

Fighting_初心的博客 357

leetcode动态规划小结-持续更新中

1.动态规划专区 300. Longest Increasing Subsequence Given an unsorted array of integers, find the length of longest increasing subsequence. For example, Given [10, 9, 2, 5, 3, 7, 101, 18], The longest ...

柳汀轩 1252

动态规划相关题目——Leetcode(持续更新)

1.打家劫舍 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。 示例 1: 输入:[1,2,3,1] 输出:4 解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。 偷窃到的最高金额 = 1 + 3 = 4 。 public i

零栀 259

LeetCode专题:贪心与动态规划(持续更新,已更38题)

持续更新LeetCode的贪心算法专题刷题总结,题目分类参考了宫水三叶https://github.com/SharingSource/LogicStack-LeetCode/wiki。

清风微浪又何妨的博客 2663
上一篇: 卡特兰数问题
下一篇: 一文掌握 Redis 必备知识
Yonghua Li
博客等级 码龄10年 38粉丝 61原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值