动态规划(Dynamic Programming)

news2024/12/23 18:31:01

动态规划(Dynamic Programming):是运筹学的一种最优化方法,只不过在计算机问题上应用比较多

DP常见步骤:

  1. 暴力递归/穷举
  2. 记忆化搜索(傻缓存 + 递归),使用备忘录/ DP Table 来优化穷举过程
  3. 严格表结构(整理缓存之间的关系,如dp[i] = dp[i - 1])

例子

509.斐波那契数

1.暴力递归
int fib(int N) {
    if (N == 1 || N == 2){
    return 1;
    }
    return fib(N - 1) + fib(N - 2);
}

2.记忆化搜索(加缓存)
int fib(int N) {
    // 备忘录全初始化为 0
    int[] memo = new int[N + 1];
    // 进行带备忘录的递归
    return dp(memo, N);
}

// 带着备忘录进行递归
int dp(int[] memo, int n) {
    // base case
    if (n == 0 || n == 1) return n;
    // 已经计算过,不用再计算了
    if (memo[n] != 0) return memo[n];
    memo[n] = dp(memo, n - 1) + dp(memo, n - 2);
    return memo[n];
}

3.严格表结构(缓存+状态转移方程)
int fib(int N) {
    if (N == 0) return 0;
    int[] dp = new int[N + 1];
    // base case
    dp[0] = 0; dp[1] = 1;
    // 状态转移
    for (int i = 2; i <= N; i++) {
        dp[i] = dp[i - 1] + dp[i - 2];
    }

    return dp[N];
}

4.空间压缩(优化)

由 状态转移方程可知,f(n) 只和 f(n-1) 和 f(n-2) 有关,使用「滚动数组思想」可以把空间复杂度优化成 O(1)

    int fib(int n) {
        if (n < 2) {
            return n;
        }
        int p = 0, q = 0, r = 1;
        for (int i = 2; i <= n; ++i) {
            p = q; 
            q = r; 
            r = p + q;
        }
        return r;
    }

基础类DP

70.爬楼梯

经典动态规划

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];
    }
}

空间压缩

class Solution {
    public int climbStairs(int n) {
        if (n == 1) {
            return 1;
        }
        int prev = 1;
        int cur = 2;
        int next = 0;
        for (int i = 3; i <= n; i++) {
            next = cur + prev;
            prev = cur;
            cur = next;
        }
        return cur;
    }
}

746.使用最小花费爬楼梯

class Solution {
    public int minCostClimbingStairs(int[] cost) {
        int[] dp = new int[cost.length + 2];
        dp[1] = 0;
        dp[2] = 0;
        for (int i = 3; i <= cost.length + 1; i++) {
            dp[i] = Math.min(dp[i - 1] + cost[i - 2], dp[i - 2] + cost[i - 3]);
        }
        return dp[cost.length + 1];
    }
}

空间压缩

class Solution {
    public int minCostClimbingStairs(int[] cost) {
        int prev = 0;
        int cur = 0;
        int next = 0;
        for (int i = 2; i <= cost.length; i++) {
            next = Math.min(prev + cost[i - 2], cur + cost[i - 1]);
            prev = cur;
            cur = next;
        }
        return cur;
    }
}

62.不同路径

class Solution {
    public int uniquePaths(int m, int n) {
        if (m <= 0 || n <= 0) {
            return 0;
        }
        int[][] dp = new int[m][n];

        // base case
        for (int i = 0; i < m; i++) {
            dp[i][0] = 1;
        }
        
        for (int i = 0; i < n; i++) {
            dp[0][i] = 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];
    }
}

 路径压缩

class Solution {
    public int uniquePaths(int m, int n) {
       if (m <= 0 || n <= 0) {
            return 0;
        }
        int[] dp = new int[n];

         // base case
        Arrays.fill(dp, 1);

        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                dp[j] += dp[j - 1];
            }
        }
        return dp[n - 1];
    }
}

63.不同路径 II

class Solution {
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        int row = obstacleGrid.length;
        int col = obstacleGrid[0].length;

        int[][] dp = new int[row][col];

        for (int i = 0; i < row; i++) {
            if (obstacleGrid[i][0] == 1){
                break;
            }
            dp[i][0] = 1;
        }

        for (int i = 0; i < col; i++) {
            if (obstacleGrid[0][i] == 1){
                break;
            }
            dp[0][i] = 1;
        }

        for (int i = 1; i < row; i++) {
            for (int j = 1; j < col; j++) {
                dp[i][j] = obstacleGrid[i][j] == 1 ? 0 : dp[i - 1][j] + dp[i][j - 1];
            }
        }
        return dp[row - 1][col - 1];
    }
}

空间压缩

class Solution {
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        int row = obstacleGrid.length;
        int col = obstacleGrid[0].length;

        if (obstacleGrid[0][0] == 1 || obstacleGrid[row - 1][col - 1] == 1) {
            return 0;
        }
        int[] dp = new int[col];
        dp[0] = 1;
        for (int j = 1; j < col; j++) {
            if (obstacleGrid[0][j] == 1) {
                break;
            }
            dp[j] = 1;
        }

        for (int i = 1; i < row; i++) {
            dp[0] = (obstacleGrid[i][0] == 1 || dp[0] == 0) ? 0 : 1;
            for (int j = 1; j < col; j++) {
                dp[j] = obstacleGrid[i][j] == 1 ? 0 : dp[j] + dp[j - 1];
            }
        }
        return dp[col - 1];
    }
}

64.最小路径和

class Solution {
    public int minPathSum(int[][] grid) {
        int row = grid.length;
        int col = grid[0].length;
        int[][] dp = new int[row][col];

        dp[0][0] = grid[0][0];
        for (int i = 1; i < row; i++) {
            dp[i][0] = grid[i][0] + dp[i - 1][0];
        }
        for (int i = 1; i < col; i++) {
            dp[0][i] = grid[0][i] + dp[0][i - 1];
        }

        for (int i = 1; i < row; i++) {
            for (int j = 1; j < col; j++) {
                dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
            }
        }
        return dp[row - 1][col - 1];
    }
}

空间压缩

class Solution {
    public int minPathSum(int[][] grid) {
        int row = grid.length;
        int col = grid[0].length;

        int[] dp = new int[col];

        dp[0] = grid[0][0];
        for (int i = 1; i < col; i++) {
            dp[i] = grid[0][i] + dp[i - 1];
        }

        for (int i = 1; i < row; i++) {
            dp[0] = dp[0] + grid[i][0];
            for (int j = 1; j < col; j++) {
                dp[j] = Math.min(dp[j], dp[j - 1]) + grid[i][j];
            }
        }
        return dp[col - 1];
    }
}

0-1背包类DP

在上述例题中,由于每个物体只有两种可能的状态(取与不取),对应二进制中的0和1,这类问题便被称为「0-1 背包问题」。

  • 01背包最重要的是如何识别出来为01背包,一般是有一个目标堆,对于数组的元素有留和舍两种选择,通过数组值的取舍进行达到目标堆的目的
  • 背包问题进行空间压缩时,weight 的循环要从大到小遍历,否则会造成前段值覆盖引起的答案错误。

416.分割等和子集

class Solution {
    public boolean canPartition(int[] nums) {
        int sum = 0;
        int max = 0;
        for (int num : nums) {
            sum += num;
            max = Math.max(max, num);
        }
        int half = sum / 2;
        if (((sum & 1) == 1) || max > half){
            return false;
        }
        
        boolean[][] dp = new boolean[nums.length][half + 1];
        // base case
        dp[0][0] = true; // 第一个元素不选,容量为0时满足的
        dp[0][nums[0]] = true; // 选择第一个元素

        for (int i = 1; i < nums.length; i++) {
            for (int j = 1; j <= half; j++) {
                // 不选择 num[i]
                dp[i][j] = dp[i-1][j];
                // 保证下标不越界
                if (j - nums[i] >= 0){
                    // 选择 num[i], 看是否能在 [0, i - 1] 这个子区间内找到一部分元素,使得它们的和为 j - nums[i]
                    dp[i][j] |= dp[i - 1][j - nums[i]];
                }
            }
            // 由于状态转移方程的特殊性,提前结束,可以认为是剪枝操作
            if (dp[i][half]) {
                return true;
            }
        }
        return dp[nums.length - 1][half];
    }
}

空间压缩

class Solution {
    public boolean canPartition(int[] nums) {
        int sum = 0;
        int max = 0;
        for (int num : nums) {
            sum += num;
            max = Math.max(max, num);
        }
        int half = sum / 2;
        if (((sum & 1) == 1) || max > half) {
            return false;
        }

        boolean[] dp = new boolean[half + 1];

        dp[0] = true;
        for (int i = 1; i < nums.length; i++) {
            for (int j = half; j >= nums[i]; j--) {
                dp[j] |= dp[j - nums[i]];
            }
            if (dp[half]) {
                return true;
            }
        }
        return dp[half];
    }
}

494.目标和

class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        int sum = 0;
        for (int num : nums) {
            sum += num;
        }
        if (Math.abs(sum) < Math.abs(target)) {
            return 0;
        }
        // 因为包含了负数和 0, range: [-sum, sum]
        int range = 2 * sum + 1;
        int[][] dp = new int[nums.length][range];

        dp[0][sum - nums[0]] += 1;
        dp[0][sum + nums[0]] += 1;
        for (int i = 1; i < nums.length; i++) {
            for (int j = -sum; j <= sum; j++) {
                if (j + nums[i] > sum) {    // 超过 [-sum, sum] 的范围,只能减
                    dp[i][j + sum] = dp[i - 1][j - nums[i] + sum];
                } else if (j - nums[i] < -sum) { // 超过 [-sum, sum] 的范围,只能加
                    dp[i][j + sum] = dp[i - 1][j + nums[i] + sum];
                } else {
                    dp[i][j + sum] = dp[i - 1][j - nums[i] + sum] + dp[i - 1][j + nums[i] + sum];
                }
            }
        }
        return dp[nums.length - 1][sum + target];
    }
}

474.一和零

class Solution {
    public int findMaxForm(String[] strs, int m, int n) {
        int[][][] dp = new int[strs.length + 1][m + 1][n + 1];

        for (int i = 1; i <= strs.length; i++) {
            int zeros = containsZero(strs[i - 1]);
            int ones = strs[i - 1].length() - zeros;
            for (int j = 0; j <= m; j++) {
                for (int k = 0; k <= n; k++) {
                    dp[i][j][k] = dp[i - 1][j][k];
                    if (j >= zeros && k >= ones){
                        dp[i][j][k] = Math.max(dp[i][j][k], dp[i - 1][j - zeros][k - ones] + 1);
                    }
                }
            }
        }
        return dp[strs.length][m][n];
    }

     private int containsZero(String str) {
        int res = 0;
        for (char c : str.toCharArray()) {
            if (c == '0') {
                res++;
            }
        }
        return res;
    }
}

空间压缩

class Solution {
    public int findMaxForm(String[] strs, int m, int n) {
        int[][] dp = new int[m + 1][n + 1];

        for (int i = 1; i <= strs.length; i++) {
            int zeros = containsZero(strs[i - 1]);
            int ones = strs[i - 1].length() - zeros;
            for (int j = m; j >= 0; j--) {
                for (int k = n; k >= 0; k--) {
                    if (j >= zeros && k >= ones){
                        dp[j][k] = Math.max(dp[j][k], dp[j - zeros][k - ones] + 1);
                    }
                }
            }
        }
        return dp[m][n];
    }

     private int containsZero(String str) {
        int res = 0;
        for (char c : str.toCharArray()) {
            if (c == '0') {
                res++;
            }
        }
        return res;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1317610.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

锁--07_2---- index merge(索引合并)引起的死锁

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 案例分析生产背景死锁日志表结构执行计划 EXPLAN为什么会用 index_merge&#xff08;索引合并&#xff09;为什么用了 index_merge就死锁了解决方案注&#xff1a;M…

SQL基础:操作环境搭建

在上一节中&#xff0c;我们简单讲述了数据库和SQL的基本概念。 本节我们讲述一下环境搭建&#xff0c;为下一节讲表的基本操作做下铺垫。 环境搭建 具体到操作&#xff0c;我们就要准备一些环境了。如果不进行练习&#xff0c;我们学习的知识将很快被遗忘。 MySQL安装&…

如何使用Lychee结合内网穿透搭建本地私人图床网站并实现远程访问

文章目录 1.前言2. Lychee网站搭建2.1. Lychee下载和安装2.2 Lychee网页测试2.3 cpolar的安装和注册 3.本地网页发布3.1 Cpolar云端设置3.2 Cpolar本地设置 4.公网访问测试5.结语 1.前言 图床作为图片集中存放的服务网站&#xff0c;可以看做是云存储的一部分&#xff0c;既可…

四舍五入浮点数

1.题目如下&#xff1a; 2.方法一&#xff1a; 直接取出小数部分第一位来判断。 1. 先乘以10。 2. 强制类型转换为整型&#xff0c;去掉小数部分。 3. 再模10&#xff0c;相当于取出原数的小数第一位。 代码实现&#xff1a; int way1(double n) {int a (int)(n * 10);int b…

kafka学习笔记--Kafka副本

本文内容来自尚硅谷B站公开教学视频&#xff0c;仅做个人总结、学习、复习使用&#xff0c;任何对此文章的引用&#xff0c;应当说明源出处为尚硅谷&#xff0c;不得用于商业用途。 如有侵权、联系速删 视频教程链接&#xff1a;【尚硅谷】Kafka3.x教程&#xff08;从入门到调优…

一些关于fMRI脑数据的预处理工具

一些关于fMRI脑数据的预处理工具 前言概述SPM12工具箱FSL工具箱FreeSurfer工具箱BrainNet Viewer工具箱circularGraph工具箱Nipype集成框架fMRIPrep集成框架参考文献 前言 March 25, 2022 这里是关于fMRI脑数据的预处理工具的相关调研 主要是关于数据的预处理&#xff0c;数据…

万兆网络之屏蔽线序接法(中)

在介绍优质网线选购之前&#xff0c;先简单介绍一下水晶头 1毛钱一颗跟1元一颗的水晶头&#xff0c;往往是金手指厚度差别&#xff0c;你可以想象压制的时候可能会有什么情况 另外&#xff0c;一些3元一颗的镀金水晶头会有15U、30U之类的是电镀厚度单位&#xff0c;数值越大镀…

【数据挖掘】国科大苏桂平老师数据库新技术课程作业 —— 第四次作业

云数据库研究 云计算与云数据库背景 云计算&#xff08;cloud computing&#xff09;是 IT 技术发展的最新趋势&#xff0c;正受到业界和学术界的广泛关注。云计算是在分布式处理、并行处理和网格计算等技术的基础上发展起来的&#xff0c;是一种新兴的共享基础架构的方法。它…

java内置的数据结构

Java语言提供了许多内置的数据结构&#xff0c;包括&#xff1a; 1. 数组&#xff08;Array&#xff09;&#xff1a;数组是最基本的数据结构之一&#xff0c;它是一个有序的元素集合&#xff0c;每个元素都有一个对应的索引。在Java中&#xff0c;数组可以通过声明和初始化来创…

2023年金属非金属矿山(地下矿山)安全管理人员证模拟考试题库及金属非金属矿山(地下矿山)安全管理人员理论考试试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2023年金属非金属矿山&#xff08;地下矿山&#xff09;安全管理人员证模拟考试题库及金属非金属矿山&#xff08;地下矿山&#xff09;安全管理人员理论考试试题是由安全生产模拟考试一点通提供&#xff0c;金属非金…

《软件方法(下)》第8章2023版8.1 分析工作流概述

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 第8章 分析 之 分析类图——知识篇 墙上挂了根长藤&#xff0c;长藤上面挂铜铃 《长藤挂铜铃》&#xff1b;词&#xff1a;元庸&#xff0c;曲&#xff1a;梅翁&#xff08;姚敏&…

手麻、腿麻、麻痛…背后竟隐藏7大疾病!多一个人知道,少一个悲剧!

手脚麻木背后的7大病症&#xff1a;骨病、脑梗、肿瘤…… 1、神经问题 上图四只手上橙色的区域代表了麻木感&#xff0c;如果您的手麻集中在无名指和小指的区域&#xff0c;您可以拿一张纸&#xff0c;用五个手指分别试着夹住&#xff0c;检验您的五个手指力量&#xff1b;您还…

软件测试之鲁棒性测试

文章目录 前言一、鲁棒性测试是什么&#xff1f;二、鲁棒性测试的目的三、测试原理3.1 错误数据处理3.2 异常情况处理 前言 Bootloader软件刷写鲁棒性(Robustness)测试是指对Bootloader软件进行连续多次的刷写测试&#xff0c;且一次Fail都没发生&#xff0c;以此验证Bootload…

MySql的增、删、改、查(MySql数据库学习——五)

增&#xff08;数据添加/插入数据&#xff09; 使用 INSERT INTO SQL 语句来插入数据。我们可以通过 mysql> 命令提示窗口中向数据表中插入数据&#xff0c;或者 通过PHP 脚本来插入数据。 sql语句&#xff1a; INSERT INTO table_name ( field1, field2,...fieldN ) …

系列九、事务

一、事务 1.1、概述 事务是一组操作的集合&#xff0c;它是一个不可分割的工作单位&#xff0c;事务会把所有的操作作为一个整体一起向系统提交或者撤销操作请求&#xff0c;即&#xff1a;这些操作要么同时成功&#xff0c;要么同时失败。 例如: 张三给李四转账1000块钱&…

UI自动化Selenium 测试报告BeautifulReport使用及修改

一、BeautifulReport安装 pip安装 pip install BeautifulReport Pycharm中安装 二、原生报告样式 原生报告&#xff0c;因为我使用ddtunittest数据驱动模式&#xff0c;所以Excel中所有参数都会被拼接出来&#xff0c;导致测试方法里面有太多不需要展示的内容&#xff1b; …

IDEA配置一个新项目

git clone xxxxx 下载项目主分支 git checkout xxx 切换到需要开发的分支上 配置maven仓库 在File下的Settings中设置maven仓库 配置maven仓库的文件夹 配置好maven后&#xff0c;项目中会出现一个红色的pom.xml文件&#xff0c;右击文件&#xff0c;点击…&#xff0c;pom…

【计算机组成与体系结构Ⅱ】多处理器部分讨论题目

多处理机课堂讨论 1.并行计算体系结构有哪些? SIMD、MIMD 2.多处理机的存储结构有哪些? 对称式共享存储器结构、分布式共享存储结构 3.什么是多处理机的一致性? 如果对某个数据项的任何读操作均可得到其最新写入的值&#xff0c;则认为这个存储系统是一致的。 4.监听协议的工…

Ubuntu 常用命令之 ln 命令用法介绍

ln命令在Ubuntu系统中用于创建硬链接或符号链接。硬链接是指向文件的物理地址&#xff0c;而符号链接&#xff08;也称为软链接&#xff09;是指向文件路径的引用。 命令格式&#xff1a;ln [选项]... [-T] 目标&#xff08;源文件&#xff09; 链接&#xff08;目标文件&…

windows下使用logstash同步跨网络集群的数据

我们在开发环境过程中&#xff0c;可能会遇到这样的场景。我们可以通过VPN访问远端的机房。有可能还要跨机房访问。这篇文章演示使用logstash&#xff0c;在windows上&#xff0c;去同步跨网络环境的不同机房之间的数据。 此方式受网络限制。适合同步小规模数据。 下载logstash…