【算法】day16 动态规划

1、爬楼梯 hot

题目70. 爬楼梯 - 力扣(LeetCode)

分析

状态表示:爬 i 阶楼梯最多有 dpi 种不同走法。

状态转移方程:i = 1 时会越界,单独处理,dp1=1;i=2 时,会获取 dp0,也单独处理,dp2 = 2。

从 index=3 开始遍历填表,dpn 就是答案。

代码:时间复杂度:O(n)。

java 复制代码
class Solution {
    public int climbStairs(int n) {
        if (n==1) return 1;
        int[] dp = new int[n+1];
        dp[1] = 1;
        dp[2] = 2;
        for (int i=3; i <= n; i++)
            dp[i] = dp[i-1]+dp[i-2];
        return dp[n];
    }
}

2、杨辉三角 hot

题目118. 杨辉三角 - 力扣(LeetCode)

分析:遍历 i=0~numRows-1 行,遍历 j=0~i 列。每行首、尾是1;其余=上方+左上方 之和。时间复杂度 O(n^2)。

状态表示:dpij 表示第 i 行第 j 列的元素值。

状态转移方程:j=0, i, dpij = 1。其余,dpij=dpi-1j+dpi-1j-1。若 i=0 会越界,单独处理 dp00=1。

代码

java 复制代码
class Solution {
    public List<List<Integer>> generate(int numRows) {
        List<List<Integer>> result = new ArrayList<>();
        // 处理第一行
        List<Integer> tmp = new ArrayList<>();
        tmp.add(1);
        result.add(tmp);
        // 处理剩余行
        for (int i = 1; i < numRows; i++) {
            tmp = new ArrayList<>();
            for (int j = 0; j <= i; j++) {
                if (j==0 || j==i) tmp.add(1);
                else {
                    List<Integer> preRow = result.get(i-1);
                    tmp.add(preRow.get(j) + preRow.get(j-1));
                }
            }
            result.add(tmp);
        }
        return result;
    }
}

3、打家劫舍 hot

题目198. 打家劫舍 - 力扣(LeetCode)

分析

状态表示:偷窃到 i 位置房屋,最多能偷多少。

状态转换方程:

i=0,1 时会越界,单独处理。dp0=nums0,dp1=max(nums0, nums1)。

代码:时间复杂度 O(n)。

java 复制代码
class Solution {
    public int rob(int[] nums) {
        int n = nums.length;
        if (n==1) return nums[0];
        int[] dp = new int[n];
        dp[0] = nums[0];
        dp[1] = Math.max(nums[0], nums[1]);
        for (int i = 2; i < n; i++)
            dp[i] = Math.max(dp[i-1], dp[i-2]+nums[i]);
        return dp[n-1];
    }
}

4、分割等和子集 hot

题目416. 分割等和子集 - 力扣(LeetCode)

分析:换一个说法,从数组中选出一些数字,让他们的和刚好为总和 sum 的一半。即 0-1 背包问题(从前 i 个物品选,每个物品只有一个(无限个就是完全背包),重量刚好为 w,的最大价值)。

状态表示:dpij 表示是否能在 0,i 中选取数字,和恰好等于 sum/2。

状态转移方程:

初始化:dp00 表示从0个数中选,刚好和为 0,true;dp0j 表示从0个数中选,刚好和为 j (j > 0),false;dpi0 表示从 i 个数中选,刚好和为0,就是不选,true。

特殊情况:n=1,不能分,返回 false;sum 为奇数,sum/2 为小数,选不出和为小数的,返回 false。

注意:dpij 表示前 i 个数中选,因此第 i 个数对应 numsi-1

时间复杂度:O(n*sum/2),空间复杂度:O(n*sum/2)

空间优化:实际上更新每一行 dp,都是从上一行的 j 前的 dp 取值计算的。我们两层循环的目的,就是防止本层跟新的值,把 j 前需要取的值给覆盖了。换个思路:倒着更新,就不会把 j 前的值给覆盖掉了。

代码

java 复制代码
class Solution {
    public boolean canPartition(int[] nums) {
        int n = nums.length;
        if (n == 1) return false;
        int sum = 0;
        for (int num : nums) sum += num;
        if (sum % 2 == 1) return false;
        sum = sum/2;

        boolean[][] dp = new boolean[n+1][sum+1];
        for (int i = 0; i <= n; i++) dp[i][0] = true;

        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= sum; j++) {
                if (j - nums[i-1] >= 0) dp[i][j] = dp[i-1][j-nums[i-1]] || dp[i-1][j];
                else dp[i][j] = dp[i-1][j];
            }
        }
        return dp[n][sum];
    }
}

方法:dp 去掉行

java 复制代码
class Solution {
    public boolean canPartition(int[] nums) {
        int n = nums.length;
        if (n == 1) return false;
        int sum = 0;
        for (int num : nums) sum += num;
        if (sum % 2 == 1) return false;
        sum = sum/2;

        boolean[] dp = new boolean[sum+1];
        dp[0] = true;

        for (int i = 1; i <= n; i++) {
            for (int j = sum; j >= nums[i-1]; j--)
                dp[j] = dp[j-nums[i-1]] || dp[j];
        }
        return dp[sum];
    }
}

5、最长有效括号 hot

题目32. 最长有效括号 - 力扣(LeetCode)

分析

dpi 表示以 i 结尾的最长有效括号,的长度。

i 处为 (,dpi 必为 0。

i 处为 ),dpi 分两种情况。

情况1,i-1 是 (:

情况2,i-1 是 ),且 m 位置是 (:

特殊处理:因为 i=0,1 时会越界,单独处理。dp0=0;dp1=2(若是 ();否则 dp1=0)。

最终结果:dp 中的最大值。

时间复杂度:O(n),空间复杂度:O(n)。

代码:注意,取 charsm 和 dpm-1 时,m 和 m-1 都不能越界。

java 复制代码
class Solution {
    public int longestValidParentheses(String s) {
        if (s.length() == 0 || s.length() == 1) return 0;
        char[] chars = s.toCharArray();
        int n = chars.length;
        int[] dp = new int[n];

        dp[0] = 0;
        if (chars[0] == '(' && chars[1] == ')') dp[1] = 2;
        else dp[1] = 0;

        int maxLen = dp[1];

        for (int i = 2; i < n; i++) {
            if (chars[i] == ')') {
                int m = i-1-dp[i-1];
                if (chars[i-1] == '(') dp[i] = dp[i-2]+2;
                else if (m >=0 && chars[m] == '(') dp[i] = 2 + dp[i-1] + (m >= 1 ? dp[m-1] : 0);
            }
            maxLen = Math.max(maxLen, dp[i]);
        }
        return maxLen;
    }
}
相关推荐
xin(n_n)b39 分钟前
经典题目(3):把数字翻译成字符串;兑换零钱
算法
惊鸿一博1 小时前
特征匹配+Glue Factory 框架评估特征提取与匹配算法时常用的度量标准
算法·特征匹配
柒和远方1 小时前
LeetCode 4. 寻找两个正序数组的中位数 —— 二分划分的艺术
javascript·python·算法
z小猫不吃鱼2 小时前
模型剪枝经典论文精读:Channel Pruning for Accelerating Very Deep Neural Networks
算法·机器学习·剪枝
njsgcs2 小时前
已知链中心距用节数算中间电机摆放位置 常量弧长 三链轮弦长模型算法
算法
2601_962440843 小时前
计算机毕业设计之jsp旅游网站管理系统
java·开发语言·算法·课程设计·旅游·推荐算法
池塘的蜗牛3 小时前
AFDM波形基于DFT的通感一体化
算法
147API4 小时前
Claude global workspace 研究给智能体测试提了一个醒
人工智能·算法·机器学习
葫三生4 小时前
《论三生原理》与模糊数学的关联、异同、互补关系?
人工智能·科技·算法·机器学习·开源
我命由我123454 小时前
方差(实例实操、与标准差的区别)
java·数据结构·算法·数据分析·java-ee·intellij-idea·idea