动态规划(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,一经查实,立即删除!

相关文章

vue3+ts封装一个button组件

创建一个新的Button组件文件 Button.vue&#xff1a; <template><button :class"buttonClass" :disabled"disabled" click"handleClick"><slot></slot><i v-if"icon" :class"icon"></i&g…

python 生成器yield

生成器 创建生成器的方式 生成器推导式yield关键字 生成器相关方法 for&#xff1a;循环遍历生成器中的每一个值next&#xff1a;获取生成器中的下一个值 生成器注意点 代码执行到yield会暂停&#xff0c;然后把结果返回出去&#xff0c;下次启动生成器会在暂停的位置继续执行…

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

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

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

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

Map Python用法:深度解析与应用探索

Map Python用法&#xff1a;深度解析与应用探索 在Python编程中&#xff0c;map() 函数是一种强大的内置高阶函数&#xff0c;用于对可迭代对象中的每个元素应用指定的函数&#xff0c;并返回一个新的迭代器&#xff0c;其中包含函数应用后的结果。本文将从四个方面、五个方面…

Bean的生命周期中有哪些对外开放的接口,及各种作用

Bean的生命周期中有哪些对外开放的接口&#xff0c;及各种作用 在 Spring 框架中&#xff0c;Bean 的生命周期可以通过一系列的回调接口来管理和控制。以下是 Spring 中对外开放的主要 Bean 生命周期接口以及它们的作用&#xff1a; InitializingBean 和 DisposableBean 接口&…

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在图像处理和计算机视觉领域的应用取得了显著进…

c++字符串相关接口

c字符串相关接口 1.str2wstr(str转换wstr)2.wstr2str(str转换wstr)3.Utf8ToAsi(Utf8转换ANSI)4.AsiToUtf8(ANSI转换Utf8)5.stringformatA/stringformatW(按照指定的格式格式化字符串)6.GetStringBetween(获取cStart cEnd之间的字符串)7.Char2Int(char转int)8.Str2Bin(字符串转换…

视觉语言大模型llava学习

1. 拉取 https://github.com/haotian-liu/LLaVA 视觉语言大模型是人工智能领域一种重要的多模态模型&#xff0c;它结合了计算机视觉&#xff08;CV&#xff09;和自然语言处理&#xff08;NLP&#xff09;的技术&#xff0c;使得模型能够同时理解图像和文本信息。这类模型在多…

hadoop部署

需要3台机子&#xff0c;Linux为centos7 分别设置静态ip&#xff0c;设置主机名,配置主机名映射&#xff0c;配置ssh免密登入 hadoop1 192.168.1.7 hadoop2 192.168.1.8 hadoop3 192.168.1.9 vi /etc/sysconfig/network-scripts/ifcfg-ens33TYPE"Ethernet" PROX…

Kotlin 泛型

文章目录 定义泛型属性泛型函数泛型类或接口 where 声明多个约束泛型具体化in、out 限制泛型输入输出 定义 有时候我们会有这样的需求&#xff1a;一个类可以操作某一类型的对象&#xff0c;并且限定只有该类型的参数才能执行相关的操作。 如果我们直接指定该类型Int&#xff…