动态规划(Dynamic-Programming)问题讲解

动态规划类问题

  • 从已知子问题的解,推导出当前问题的解 推导过程可以表达为一个数学公式
  • 用一维或二维数组来保存之前的计算结果(可以进一步降维优化)

        将当前问题 分解成子问题 ,找出递归公式,分阶段进行求解 求解过程中缓存子问题的解,避免重复计算。

以一个简单例子Fibonacci数列为例,求Fibonacci数列第 n 项的

从第二项开始,每一项的值都等于前两项的值之和
0 1 1 2 3 5 8
dp[i] = dp[i-1] + dp[i-2]

思路:

        1.若求第 0 项或第 1 项的值,直接返回对应的值 0 或 1

        2.创建一个一维数组缓存第n项数值之前的求解结果,并初始化第一项和第二项的值

        3.使用循环计算处每一项的值,直到第 n 项,最后返回一维数组的第n项值即可

代码:

public static int fibonacci(int n) {if (n == 0) {return 0;} else if (n == 1) {return 1;}int[] dp = new int[n + 1];dp[0] = 0;dp[1] = 1;for (int i = 2; i <= n; i++) {dp[i] = dp[i - 1] + dp[i - 2];}return dp[n];
}

测试:

public static void main(String[] args) {// 0 1 1 2 3 5 8System.out.println(fibonacciDown(13)); //求第 7 项值
}

降维优化:对于每个子问题只需要三个值参与,何不用三个变量代替一维数组进行优化:

    /*** 求 Fibonacci 的第 n 项 (降维)** @param n 第几项* @return*/public static int fibonacciDown(int n) {if (n == 0) {return 0;} else if (n == 1) {return 1;}int a = 0, b = 1;for (int i = 2; i <= n; i++) {int c = a + b; //记录第i项的值//更新值    a = b;b = c;}return b;}

Leecode62. 不同路径 题

从start走到finish有多少种走法(只能向右向下走)

 将每个格子的走法都记录出来,标识数字为 start 到该格子上的有多少走法,,找出规律

可看出规律为:dp[i][j] = dp[i][j - 1] + dp[i -1][j],并且第一行和第一列的值都为1,即走法只有一种

思路:

        1.以一个二维数组缓存每个格子的走法数

        2.遍历每行每列,求出每个格子的走法数

        3.最后返回第m行第n列的值,即为最终结果

代码:

    /*** 求到第m行n列有多少种走法,只能向右和向下** @param m* @param n* @return*/public static int uniquePaths2(int m, int n) {int[][] dp = new int[m][n];//初始化第一行和第一列的值为 1(其走法只有一种)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][j - 1] + dp[i - 1][j];}}print(dp);return dp[m - 1][n - 1];}

降维优化:

每次计算当前格子的走法数时,只需要上一个格子和左边格子的走法之和,何不使用一维数组,上一个格子的走法即为当前格子的做法,将dp[i][j] = dp[i][j - 1] + dp[i -1][j]改为dp[j] = dp[j] + dp[j - 1],实现降维优化的目的,以第二行到第三行为例:

代码:

    /*** 求到第m行n列有多少种走法,只能向右和向下 (降维,二维 变 一维)** @param m* @param n* @return*/public static int uniquePaths(int m, int n) {int[] dp = new int[n];//初始化第一行和第一列的值为 1(其走法只有一种)Arrays.fill(dp, 1);for (int i = 1; i < m; i++) {for (int j = 1; j < n; j++) {dp[j] = dp[j] + dp[j - 1]; //自己加上 上一列的}}
//        System.out.println(Arrays.toString(dp));return dp[n - 1];}

2. 01背包问题 - AcWing题库

从N个物体中选择物体放入体积为V的背包中,使得价值最大,每种物品只能选择一次

以一下测试示例:

4 5    //物体数量为 4,背包体积为 5
1 2   //第一个物体的体积 1 和价值 2
2 4
3 4
4 5

以一个二维数组缓存第一行只有物品A的选择,第二行只有物体A、B时的选择等..,

ABCD分别表示四个物体

二维数组 dp 中:

第一行中选择物体A,体积为1,在体积为0时不能放下为0,其它都能放下A

第二行中选择物体B,体积为2,在背包体积为0、1时不能放下,将上一行数据复制下来,背包体积为2时能放下,价值为2比上一行的A更大,为3、4、5时可以放下B此外还能放下一个物体A

第三行中选择物体C,体积为3,在背包体积为0、1、2时不能放下,将上一行数据复制下来,在背包体积 为3是虽然能放下C,但是上一行的BA价值是6,比C的价值大,所以直接复制下来,在体积为5时,当前背包除了能放下BA外还能放下一个C

第四行选择物体D,体积为4,在背包体积为0、1、2、3时不能放下,将上一行数据复制下来,在背包体积 为4是虽然能放下D,但是上一行的BA价值是6,比D的价值大,所以直接复制下来,在体积为5时同理

            编号 体积(g)  价值(元)     物品名1     1         2         A2     2         4         B3     3         4         C4     4         5         D
二维数组 dp :0   1   2   3   4    5  --> 背包体积0  0   A   A   A   A    A       A1  0   A   B   BA  BA   BA      B2  0   A   B   BA  BA   BAC     C3  0   A   B   BA  BA   BAC     D0, 2, 2, 2, 2, 20, 2, 4, 6, 6, 60, 2, 4, 6, 6, 80, 2, 4, 6, 6, 8结果:8 (BAC)

总结一个规律:

1.装得下 —— 当前物品价值比上一行价值更大时,选择当前物品,再加上总体积 - 当前物品体积得到的体积值列中最大价值得物品,加到当前处dp[i][j] = Math.max(dp[i-1][j],currItemValue + dp[i-1][jTotal - currItem.weight]) 比如dp数组中:dp[1][3] = Max(dp[0][3],B + dp[0][3-2]) = BA
2.装不下,将上一行物品复制下来dp[i][j] = dp[i-1][j]

代码二维:

import java.util.Scanner;/*** 0 - 1背包问题*/
public class Main {public static void main(String[] args) {
/*编号 体积(g)  价值(元)     物品名1     1         2         A2     2         4         B3     3         4         C4     4         5         D0   1   2   3   4    5  --> 体积0  0   A   A   A   A    A       A1  0   A   B   BA  BA   BA      B2  0   A   B   BA  BA   BAC     C3  0   A   B   BA  BA   BAC     D0, 2, 2, 2, 2, 20, 2, 4, 6, 6, 60, 2, 4, 6, 6, 80, 2, 4, 6, 6, 8结果:8 (BAC)1.装得下 —— 当前物品价值比上一行价值更大时,选择当前物品,再加上总体积 - 当前物品体积量得到得体积列中最大价值得物品,加到当前处dp[i][j] = Max(dp[i-1][j],currItemValue + dp[i-1][jTotal - currItem.weight]) 比如:dp[3][8] = Max(dp[2][8],D + dp[2][8-1]) = DA2.装不下,将上一行物品复制下来dp[i][j] = dp[i-1][j]*/Scanner sc = new Scanner(System.in);int N = sc.nextInt(); //物品数量int V = sc.nextInt(); //背包容积int[] vArr = new int[N]; //N个物品的体积int[] pArr = new int[N]; //N个物品的价值for (int i = 0; i < N; i++) {vArr[i] = sc.nextInt();pArr[i] = sc.nextInt();}System.out.println(knapsackProblem01(V, vArr, pArr, N));}public static int knapsackProblem01(int V, int[] vArr, int[] pArr, int N) {int[][] dp = new int[N][V + 1];for (int j = 0; j < V + 1; j++) {if (vArr[0] <= j) { //装得下dp[0][j] = pArr[0];}}for (int i = 1; i < N; i++) {for (int j = 0; j < V+1; j++) {int x = dp[i - 1][j]; //上一行的价值if (vArr[i] <= j) { //装得下//                      当前物品价值      剩余物品价值dp[i][j] = Math.max(x, pArr[i] + dp[i-1][j - vArr[i]]);} else { //装不下dp[i][j] = x;}}}return dp[N - 1][V];}}

 代码(降维成一维数组):

    public static void main(String[] args) {Scanner sc = new Scanner(System.in);int N = sc.nextInt(); //物品数量int V = sc.nextInt(); //背包容积int[] vArr = new int[N]; //N个物品的体积int[] pArr = new int[N]; //N个物品的价值for (int i = 0; i < N; i++) {vArr[i] = sc.nextInt();pArr[i] = sc.nextInt();}int[] dp = new int[V + 1];for (int j = 0; j < V + 1; j++) {if (vArr[0] <= j) { //装得下dp[j] = pArr[0];} else { //装不下dp[j] = 0;}}//System.out.println(Arrays.toString(dp));for (int i = 1; i < N; i++){for (int j = V; j > 0; j--) {if (vArr[i] <= j) { //装得下//     pArr[i]当前物品价值   dp[j - vArr[i]]剩余空间在上次中(避免同一物品重复使用)能装的最大值dp[j] = Math.max(dp[j], pArr[i] + dp[j - vArr[i]]);}}//System.out.println(Arrays.toString(dp));}System.out.println(dp[V]);}

3. 完全背包问题 - AcWing题库

与01背包问题的区别:

dp[i][j] = Math.max(x, pArr[i] + dp[i][j - vArr[i]]); //完全背包中物品数量无限,从本次物品中找,同一物品可重复使用

dp[i][j] = Math.max(x, pArr[i] + dp[i-1][j - vArr[i]]); //01背包中物品数量只有一个,从上次物品中找,同一物品只能用一次

代码(二维):

import java.util.Scanner;/*** 完全背包问题*/
public class Main {public static void main(String[] args) {
/*编号 体积(g)  价值(元)     物品名1     1         2         A2     2         4         B3     3         4         C4     4         5         D完全背包:0   1   2    3       4       5  --> 体积0  0   A   AA   AAA     AAAA    AAAAA       A1  0   A   B    BA      BB      BBA         B2  0   A   B    BA      BB      BBA         C3  0   A   B    BA      BB      BBA         D0      2      4      6      8     10      A0      2      4      6      8     10      B0      2      4      6      8     10      C0      2      4      6      8     10      D结果:10 (BAC)1.装得下 —— 当前物品价值比上一行价值更大时,选择当前物品,再加上(总体积 - 当前物品体积)得到的体积列中最大价值得物品,加到当前处dp[i][j] = Max(dp[i-1][j],currItemValue + dp[i-1][jTotal - currItem.weight]) 比如:dp[3][8] = Max(dp[2][8],D + dp[2][8-1]) = DA2.装不下,将上一行物品复制下来dp[i][j] = dp[i-1][j]*/Scanner sc = new Scanner(System.in);int N = sc.nextInt(); //物品数量int V = sc.nextInt(); //背包容积int[] vArr = new int[N]; //N个物品的体积int[] pArr = new int[N]; //N个物品的价值for (int i = 0; i < N; i++) {vArr[i] = sc.nextInt();pArr[i] = sc.nextInt();}System.out.println(knapsackProblemComplete(V, vArr, pArr, N));}public static int knapsackProblemComplete(int V, int[] vArr, int[] pArr, int N) {int[][] dp = new int[N][V + 1];for (int j = 0; j < V + 1; j++) {if (vArr[0] <= j) { //装得下dp[0][j] = pArr[0] + dp[0][j - vArr[0]];}}for (int i = 1; i < N; i++) {for (int j = 0; j < V + 1; j++) {int x = dp[i - 1][j]; //上一行的价值if (vArr[i] <= j) { //装得下//                    当前物品价值      剩余体积的物品价值(从本次中找)dp[i][j] = Math.max(x, pArr[i] + dp[i][j - vArr[i]]);} else { //装不下dp[i][j] = x;}}}return dp[N - 1][V];}
}

降维:

import java.util.Scanner;/*** 完全背包问题*/
public class Main {public static void main(String[] args) {
/*1. n个物品都是固体,有重量和价值2. 现在你要取走不超过 10克 的物品3. 每件物品只能使用一次编号 体积(g)  价值(元)     物品名1     1         2         A2     2         4         B3     3         4         C4     4         5         D完全背包:0   1   2    3       4       5  --> 体积0  0   A   AA   AAA     AAAA    AAAAA       A1  0   A   B    BA      BB      BBA         B2  0   A   B    BA      BB      BBA         C3  0   A   B    BA      BB      BBA         D0      2      4      6      8     10      A0      2      4      6      8     10      B0      2      4      6      8     10      C0      2      4      6      8     10      D结果:10 (BAC)1.装得下 —— 当前物品价值比上一行价值更大时,选择当前物品,再加上总重量 - 当前物品重量得到得重量列中最大价值得物品,加到当前处dp[i][j] = Max(dp[i-1][j],currItemValue + dp[i-1][jTotal - currItem.weight]) 比如:dp[3][8] = Max(dp[2][8],D + dp[2][8-1]) = DA2.装不下,将上一行物品复制下来dp[i][j] = dp[i-1][j]
4 5
1 2
2 4
3 4
4 5*/Scanner sc = new Scanner(System.in);int N = sc.nextInt(); //物品数量int V = sc.nextInt(); //背包容积int[] vArr = new int[N]; //N个物品的体积int[] pArr = new int[N]; //N个物品的价值for (int i = 0; i < N; i++) {vArr[i] = sc.nextInt();pArr[i] = sc.nextInt();}System.out.println(knapsackProblemComplete2(V, vArr, pArr, N));}/*** 取total重量的物品 并且 价值最大(降维)** @return 最大价值*/public static int knapsackProblemComplete2(int V, int[] vArr, int[] pArr, int N) {int[] dp = new int[V + 1];for (int j = 0; j < V + 1; j++) {if (vArr[0] <= j) { //装得下dp[j] = pArr[0] + dp[j - vArr[0]];}}for (int i = 1; i < N; i++) {for (int j = 0; j < V + 1; j++) {int x = dp[j]; //上一行的价值if (vArr[i] <= j) { //装得下//                      当前物品价值      剩余物品价值dp[j] = Math.max(x, pArr[i] + dp[j - vArr[i]]);} else { //装不下dp[j] = x;}}
//            System.out.println(Arrays.toString(dp));}return dp[V];}
}

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

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

相关文章

进程间通信(27000字超详解)

&#x1f30e;进程间通信 文章目录&#xff1a; 进程间通信 进程间通信简介       进程间通信目的       初识进程间通信       进程间通信的分类 匿名管道通信       认识管道       匿名管道       匿名管道测试       管道的四种…

第十五课,海龟画图:抬笔与落笔函数、画曲线函数

一&#xff0c;turtle.penup()和turtle.pendown()&#xff1a;抬起与落下画笔函数 当使用上节课学习的这个turtle.forward()&#xff1a;画笔前进函数时&#xff0c;画笔会朝着当前方向在画布上留下一条指定&#xff08;像素&#xff09;长度的直线&#xff0c;但你可能发现&a…

C++|set、map模拟实现<——红黑树

目录 一、红黑树的迭代器 1.1红黑树迭代器框架 1.2operator*() && operator->() 1.3operator() 1.4operator--() 1.5operator() && operator!() 1.6begin() && end() 二、如何用红黑树搭配map和set(仿函数) 三、红黑树封装map和set(简易版…

springboot + Vue前后端项目(第十三记)

项目实战第十三记 写在前面1.建立角色表2. 后端代码生成2.1 RoleController 3. 前端页面的搭建3.1 Role.vue3.2 路由3.3 Aside.vue3.4 页面效果 4.建立菜单表5.后端代码编写5.1 Menu5.2 MenuController 6.前端页面的搭建6.1 Menu.vue6.2 路由6.3 Aside.vue6.4 页面效果 总结写在…

keepalived安装文档

目录 1、安装环境 2、安装keepalived 2.1 上传keepalived安装文件 2.2 解压 2.3 安装keepalived 2.4 加入开机启动&#xff1a; 2.5 配置日志文件 2.6 打开防火墙的通讯地址 1、安装环境 su - root yum -y install kernel-devel* yum -y install openssl-* yum -y …

vx小程序初学

小程序初学 在我还没接触到微信小程序之前&#xff0c;通常使用轮播要么手写或使用swiper插件去实现&#xff0c;当我接触到微信小程序之后&#xff0c;我看到了微信小程序的强大之处&#xff0c;让我为大家介绍一下吧&#xff01; swiper与swiper-item一起使用可以做轮播图 …

把自己的服务器添加到presearch节点

Presearch is a scam. Before, judging by the price of the token you should have been able to get between $150-$200 after 12-13 months of regular searches. "If you use this service for the next 11 years you will have earned $30!" Presearch大约需要…

Easy RoCE:在SONiC交换机上一键启用无损以太网

RDMA&#xff08;远程直接内存访问&#xff09;技术是一种绕过 CPU 或操作系统&#xff0c;在计算机之间直接传输内存数据的技术。它释放了内存带宽和 CPU&#xff0c;使节点之间的通信具有更低的延迟和更高的吞吐量。目前&#xff0c;RDMA 技术已广泛应用于高性能计算、人工智…

车流量监控系统

1.项目介绍 本文档是对于“车流量检测平台”的应用技术进行汇总&#xff0c;适用于此系统所有开发&#xff0c;测试以及使用人员&#xff0c;其中包括设计背景&#xff0c;应用场景&#xff0c;系统架构&#xff0c;技术分析&#xff0c;系统调度&#xff0c;环境依赖&#xf…

MongoDB~存储引擎了解

存储引擎 存储引擎是一个数据库的核心&#xff0c;主要负责内存、磁盘里数据的管理和维护。 MongoBD的优势&#xff0c;在于其数据模型定义的灵活性、以及可拓展性。但不要忽略&#xff0c;其存储引擎也是插件式的存在&#xff0c;支持不同类型的存储引擎&#xff0c;使用不同…

导线防碰撞警示灯:高压线路安全保障

导线防碰撞警示灯&#xff1a;高压线路安全保障 在广袤的大地上&#xff0c;高压线路如同血脉般纵横交错&#xff0c;然而&#xff0c;在这看似平静的电力输送背后&#xff0c;却隐藏着不容忽视的安全隐患。特别是在那些输电线路跨越道路、施工等区域的路段&#xff0c;线下超…

顶点着色技术在AI去衣中的作用

在当今的数字时代&#xff0c;人工智能&#xff08;AI&#xff09;已经渗透到我们生活的方方面面&#xff0c;从智能家居到自动驾驶汽车&#xff0c;再到在线购物推荐。然而&#xff0c;AI的影响远不止于此。近年来&#xff0c;AI在图像处理和计算机视觉领域的应用取得了显著进…

【Python系列】Python 中方法定义与方法调用详解

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

详细介绍运算符重载函数,清晰明了

祝各位六一快乐~ 前言 1.为什么要进行运算符重载&#xff1f; C中预定义的运算符的操作对象只能是基本数据类型。但实际上&#xff0c;对于许多用户自定义类型&#xff08;例如类&#xff09;&#xff0c;也需要类似的运算操作。这时就必须在C中重新定义这些运算符&#xff…

短信发送验证码及邮件发送验证码

发送短信验证码 阿里云发送验证码 public Integer sendTelCode(String tel) {String url "https://dfsns.market.alicloudapi.com/data/send_sms";String appcode "a3198282fbdf443d97aa9f3cfbe1232e";int code RandomUtil.randomInt(1000,10000);emai…

【DSP】xDAIS算法标准

1. 简介 在安装DSP开发支持包时&#xff0c;有名为 “xdais_7_21_01_07”文件夹。xDAIS全称: TMS320 DSP Algorithm Standard(算法标准)。39条规则&#xff0c;15条指南。参考文档。参考文章。 2. 三个层次 3.接口 XDAIS Digital Media。编解码引擎。VISA&#xff08;Video&…

牛客小白月赛95VP

早上蓝桥杯大寄&#xff0c;算是交了300元买了件T恤qaq 1.签到&#xff1a;https://ac.nowcoder.com/acm/contest/83687/A 下面是AC代码&#xff1a; #include<bits/stdc.h> using namespace std; int main() {int a,b;cin>>a>>b;if(ab) cout<<&quo…

qi5uxeel算法分析流程记录libmsec.so

动态注册函数主要方法在so层。 libmsec.so 通过regsiterNative方法注册62个函数 加壳混淆ollvm动态反调试等你还能再恶心点不 分析流程定位关键点 算法设计SM4以及各类自定义签名算法 涉及到的知识包含Java C Android 完整混淆流程如下图&#xff0c; 不得不说你开发的…

C语言 指针——函数指针

目录 什么是函数指针&#xff1f; 函数指针的定义 定义函数指针时的常见错误 函数指针有什么用&#xff1f; 函数指针的主要应用 什么是函数指针&#xff1f; 函数指针 (Function Pointer) 就是指向函数的指针变量 数据类型 ( * 指针变量名 ) ( 形参列表 ); 例如&#x…

el-date-picker 选择日期范围只保存左侧日期面板

需求 日期筛选&#xff0c;但限制只能选择同一个月的数据&#xff0c;故此应该去掉右侧月份面板。 实现 主要是通过 css 样式实现&#xff1a; <style> /* 隐藏右边日期面板 */ .el-picker-panel__content.el-date-range-picker__content.is-right .el-date-table, .…