【算法】动态规划

文章目录

  • 概述
  • 背包问题
    • 01背包问题:
    • 代码示例
    • 部分背包
      • 代码示例
    • 完全背包
      • 代码示例
    • 多重背包
      • 代码示例
  • 总结提升

概述

动态规划(Dynamic Programming)是一种通过将问题划分为相互重叠的子问题来解决问题的算法思想。其核心思想是通过保存已经计算过的子问题的解,避免重复计算,从而降低时间复杂度。

动态规划的适用条件包括:

问题具有最优子结构:问题的最优解可以通过子问题的最优解来构造。
子问题之间存在重叠:原问题的求解过程中,多次求解相同的子问题。
动态规划的基本步骤如下:

1、定义状态:明确问题的状态,并用状态变量表示。
2、确定状态转移方程:根据问题的最优子结构,确定状态之间的转移关系。
3、初始化:设置初始状态的值。
4、递推计算:根据状态转移方程,从初始状态逐步计算到目标状态。
5、求解目标:根据最终状态的值,得到问题的解。

背包问题

背包问题是一个经典的组合优化问题,在计算机科学和运筹学中具有广泛的应用。它的基本形式是:给定一个固定大小的背包,和一组物品,每个物品有对应的重量和价值。目标是在不超过背包容量的前提下,选择合适的物品放入背包,使得背包中物品的总价值最大化。

背包问题可以分为多个不同的变体,其中最常见的有01背包问题、部分背包、完全背包问题和多重背包问题。

01背包问题:

每个物品要么放入背包,要么不放入背包,不能拆分。
对于每个物品,只有两种选择,放入或者不放入背包。

代码示例

public class Knapsack {public static int knapsack(int[] weights, int[] values, int capacity) {int n = weights.length; // 物品个数int[][] dp = new int[n + 1][capacity + 1]; // 创建动态规划表for (int i = 1; i <= n; i++) {for (int j = 1; j <= capacity; j++) {if (weights[i - 1] > j) {dp[i][j] = dp[i - 1][j];} else {dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weights[i - 1]] + values[i - 1]);}}}return dp[n][capacity];}public static void main(String[] args) {int[] weights = {2, 3, 4, 5}; // 物品重量int[] values = {3, 4, 5, 6};  // 物品价值int capacity = 8;            // 背包容量int max_value = knapsack(weights, values, capacity);System.out.println("最大价值: " + max_value);}
}

这段代码实现了 01 背包问题的动态规划解法。knapsack 方法接受物品重量数组 weights、物品价值数组 values 和背包容量 capacity 作为输入,并返回最大的背包价值。

代码中创建了一个二维数组 dp 来存储子问题的最优解。通过两层循环遍历每个子问题,并根据状态转移方程更新 dp 数组。最后,返回 dp[n][capacity],即表示最大的背包价值。

在上述示例中,测试样例中的物品重量为 [2, 3, 4, 5],物品价值为 [3, 4, 5, 6],背包容量为 8。输出结果为最大价值为 11。

部分背包

部分背包问题是一个在给定背包容量的限制下,选择物品放入背包以使得背包的总价值最大化的问题。与 0-1 背包问题不同的是,部分背包问题允许将物品分割成小块,并且可以选择装入一部分物品。

在部分背包问题中,每个物品都有一个重量和一个价值。目标是选择一些物品装入背包,使得被选中物品的总重量不超过背包的容量,同时使得它们的总价值最大化。

为了解决这个问题,我们可以根据物品的单位价值(即每单位重量的价值)进行排序,然后按照从高到低的顺序依次装入物品,直到背包无法再装入完整的物品为止。如果背包仍有剩余容量,则根据物品的单位价值将其分割成部分,并将部分装入背包,使得装入的部分物品价值最大化。

部分背包问题可以使用贪心算法来求解。通过按照单位价值排序并可根据限制条件逐步装入物品,贪心算法能够在较短的时间内得到一个近似最优解。

需要注意的是,部分背包问题的贪心算法并不一定能够得到最优解。在某些情况下,贪心选择可能会导致次优解或者错误的结果。因此,对于严格要求最优解的问题,可能需要使用其他算法来求解,如动态规划等。

代码示例

public class FractionalKnapsack {public static double fractionalKnapsack(int[] weights, int[] values, int capacity) {int n = weights.length;double[][] dp = new double[n + 1][capacity + 1];for (int i = 1; i <= n; i++) {int weight = weights[i - 1];int value = values[i - 1];for (int j = 1; j <= capacity; j++) {if (weight <= j) {// 可以完整装入当前物品dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight] + value);} else {// 只能装入一部分当前物品dp[i][j] = dp[i - 1][j];}}}return dp[n][capacity];}public static void main(String[] args) {int[] weights = {2, 3, 4, 5};int[] values = {3, 4, 5, 6};int capacity = 8;double max_value = fractionalKnapsack(weights, values, capacity);System.out.println("最大总价值: " + max_value);}
}

这段代码使用动态规划算法解决部分背包问题。weights 数组存储物品的重量,values 数组存储物品的价值,capacity 表示背包的容量。

通过创建一个二维数组 dp,其中 dp[i][j] 表示在考虑前 i 个物品,并且背包容量为 j 时的最大总价值。

代码中,使用两层循环遍历所有物品和背包容量的组合。对于每个物品,分为两种情况进行处理:

如果当前物品的重量小于等于背包容量,可以选择完整装入该物品。此时,最大总价值等于不装入该物品时的最大总价值 dp[i-1][j] 和装入该物品后的总价值 dp[i-1][j-weight] + value 中的较大值。
如果当前物品的重量大于背包容量,无法完整装入该物品。此时,最大总价值与上一个物品时的价值相同,即 dp[i][j] = dp[i-1][j]。
最后,返回二维数组 dp 中最后一个元素的值,即表示在考虑所有物品时,背包可以装入的最大总价值。

在上述示例中,测试样例中的物品数组包含四个物品,每个物品的重量和价值分别为 2、3、4、5 和 3、4、5、6,背包容量为 8。输出结果为最大总价值为 11.0。

完全背包

完全背包问题是一个在给定背包容量的限制下,选择物品放入背包以使得背包的总价值最大化的问题。与 0-1 背包问题和部分背包问题不同的是,完全背包问题允许将物品无限次地装入背包中。

在完全背包问题中,每个物品都有一个重量和一个价值。与部分背包问题类似,目标是选择一些物品装入背包,使得被选中物品的总重量不超过背包的容量,同时使得它们的总价值最大化。

为了解决这个问题,我们可以使用动态规划算法。通过创建一个二维数组 dp,其中 dp[i][j] 表示在考虑前 i 个物品,并且背包容量为 j 时的最大总价值。

与部分背包问题不同的是,在完全背包问题中,对于每个物品,我们可以选择装入 0 个、1 个、2 个,… 直到 j/weight[i] 个(j/weight[i] 向下取整)个物品。

因此,对于每个物品 i,我们可以使用以下递推关系来计算 dp[i][j]:

dp[i][j] = max(dp[i-1][j-k*weight[i]] + k*value[i]),其中 0 <= k <= j/weight[i]

遍历所有物品和背包容量的组合,通过上述递推关系更新 dp 数组中的值。

最后,返回二维数组 dp 中最后一个元素的值,即表示在考虑所有物品时,背包可以装入的最大总价值。

需要注意的是,完全背包问题的动态规划解法时间复杂度较高,并且可能需要大量的内存空间。为了提高效率,我们可以使用一维数组进行优化,在遍历物品时从小到大的顺序更新 dp 数组。

代码示例

public class CompleteKnapsack {public static int completeKnapsack(int[] weights, int[] values, int capacity) {int n = weights.length;int[] dp = new int[capacity + 1];for (int i = 0; i < n; i++) {int weight = weights[i];int value = values[i];for (int j = weight; j <= capacity; j++) {dp[j] = Math.max(dp[j], dp[j - weight] + value);}}return dp[capacity];}public static void main(String[] args) {int[] weights = {2, 3, 4, 5};int[] values = {3, 4, 5, 6};int capacity = 8;int max_value = completeKnapsack(weights, values, capacity);System.out.println("最大总价值: " + max_value);}
}

这段代码使用动态规划算法解决完全背包问题。weights 数组存储物品的重量,values 数组存储物品的价值,capacity 表示背包的容量。

通过创建一个一维数组 dp,其中 dp[j] 表示在考虑前所有物品,并且背包容量为 j 时的最大总价值。

代码中,首先遍历所有物品,然后遍历所有可能的容量,对于每个容量 j,计算出背包可以装入的最大总价值。

递推公式为:

dp[j] = max(dp[j], dp[j - weight] + value)

其中,weights[i] 表示第 i 个物品的重量,values[i] 表示第 i 个物品的价值。

在更新 dp[j] 的值时,我们将 dp[j-weight]+value 的值与 dp[j] 的值比较,取两者中的最大值作为新的 dp[j] 值。

最后,返回一维数组 dp 中最后一个元素的值,即表示在考虑所有物品时,背包可以装入的最大总价值。

在上述示例中,测试样例中的物品数组包含四个物品,每个物品的重量和价值分别为 2、3、4、5 和 3、4、5、6,背包容量为 8。输出结果为最大总价值为 18。

多重背包

多重背包问题是在给定背包容量的限制下,选择物品放入背包以使得背包的总价值最大化的问题。与完全背包问题类似,多重背包问题允许将某些物品选择多次放入背包中,但是每个物品的选择次数是有限制的。

在多重背包问题中,每个物品都有一个重量、价值和一个数量限制。目标是选择一些物品放入背包,使得被选中物品的总重量不超过背包的容量,同时使得它们的总价值最大化。

为了解决多重背包问题,我们可以使用动态规划算法。通过创建一个二维数组 dp,其中 dp[i][j] 表示在考虑前 i 个物品,并且背包容量为 j 时的最大总价值。

与完全背包问题类似,对于每个物品 i,我们需要考虑选择物品 i 的次数。假设该物品的选择次数上限为 k,则选择次数的范围是 0 到 min(k, j/weight[i])。

因此,对于每个物品 i,我们可以使用以下递推关系来计算 dp[i][j]:

dp[i][j] = max(dp[i-1][j-k*weight[i]] + k*value[i]),其中 0 <= k <= min(k, j/weight[i])

遍历所有物品和背包容量的组合,通过上述递推关系更新 dp 数组中的值。

最后,返回二维数组 dp 中最后一个元素的值,即表示在考虑所有物品时,背包可以装入的最大总价值。

需要注意的是,多重背包问题的动态规划解法时间复杂度较高,并且可能需要大量的内存空间。为了提高效率,我们可以使用一维数组进行优化,在遍历物品时从大到小的顺序更新 dp 数组。

代码示例

public class MultipleKnapsack {public static int multipleKnapsack(int[] weights, int[] values, int[] counts, int capacity) {int n = weights.length;int[] dp = new int[capacity + 1];for (int i = 0; i < n; i++) {int weight = weights[i];int value = values[i];int count = counts[i];for (int j = capacity; j >= weight; j--) {for (int k = 1; k <= count && k * weight <= j; k++) {dp[j] = Math.max(dp[j], dp[j - k * weight] + k * value);}}}return dp[capacity];}public static void main(String[] args) {int[] weights = {2, 3, 4};int[] values = {3, 4, 5};int[] counts = {2, 3, 1};int capacity = 8;int max_value = multipleKnapsack(weights, values, counts, capacity);System.out.println("最大总价值: " + max_value);}
}

这段代码使用动态规划算法解决多重背包问题。weights 数组存储物品的重量,values 数组存储物品的价值,counts 数组存储每个物品的数量限制,capacity 表示背包的容量。

通过创建一个一维数组 dp,其中 dp[j] 表示在考虑前所有物品,并且背包容量为 j 时的最大总价值。

代码中,首先遍历所有物品,然后通过两层循环遍历背包容量和物品的选择次数。对于每个物品,计算出选择不同次数情况下的最大总价值。

递推公式为:

dp[j] = max(dp[j], dp[j-k*weight]+k*value),其中 1 <= k <= min(count, j/weight)

在更新 dp[j] 的值时,我们将 dp[j-kweight]+kvalue 的值与 dp[j] 的值比较,取两者中的最大值作为新的 dp[j] 值。

最后,返回一维数组 dp 中最后一个元素的值,即表示在考虑所有物品时,背包可以装入的最大总价值。

在上述示例中,测试样例中的物品数组包含三个物品,每个物品的重量和价值分别为 2、3、4 和 3、4、5,数量限制分别为 2、3、1,背包容量为 8。输出结果为最大总价值为 20。

总结提升

在这里插入图片描述

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

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

相关文章

网络工程师怎么才算学好

学网络工程师怎么样才算学好&#xff1f;这个朋友他说他现在准备跟我学这个网络工程师。他说他理解的网络工程师就是要不断的在技术上进行积累&#xff0c;至于产品还有客户&#xff0c;他不知道该怎么样以后去学习。我先说一下这个网络工程师你在课程或者技术上学习&#xff0…

Academic accumulation|社会创业研究:过去的成就和未来的承诺

文献来源&#xff1a;Saebi T, Foss N J, Linder S. Social entrepreneurship research: Past achievements and future promises[J]. Journal of management, 2019, 45(1): 70-95. 一、文章介绍 &#xff08;一&#xff09;文章主要包含什么&#xff1f; SE越来越受到学术界的…

中睿天下荣获2023全国智能驾驶测试赛车联网安全比赛第一名

9月24日&#xff0c;由工业和信息化部、公安部、交通运输部、中国科学技术协会、北京市人民政府共同主办的2023世界智能网联汽车大会展览会在北京闭幕。同期举行的全国智能驾驶测试赛&#xff08;京津冀赛区&#xff09;宣布比赛结果&#xff0c;中睿天下凭借过硬的产品实力&am…

了解”变分下界“

“变分下界”&#xff1a;在变分推断中&#xff0c;我们试图找到一个近似概率分布q(x)来逼近真实的概率分布p(x)。变分下界是一种用于评估近似概率分布质量的指标&#xff0c;通常用来求解最优的近似分布。它的计算涉及到对概率分布的积分或期望的估计

[中间件~大厂面试题] 腾讯三面,40亿的QQ号如何去重

前言&#xff1a; 在Spring Boot框架下&#xff0c;可以使用以下方法来去重40亿个QQ号.请注意&#xff1a;QQ号码的理论最大值为 2 32 − 1 2^{32} - 1 232−1&#xff0c;大概是43亿左右。 文章目录 提前总结(总分总&#xff5e;&#xff5e;&#xff5e;)最粗鲁的方式1. 使用…

1、手把手教你学会使用 FlinkSQL客户端

目录 1、FlinkSQL客户端的功能 2、FlinkSQL客户端启动参数配置 2.1 基本语法 2.2 相关参数([MODE])&#xff1a; 2.3 相关参数(embedded [OPTIONS])&#xff1a; 3、启动Flink的sql-client 3.1 启动时使用初始化脚本 3.2 启动时指定依赖的jar包 3.3 基于yarn-session模…

解决java.io.FileNotFoundException: HADOOP_HOME and hadoop.home.dir are unset.的错误

文章目录 1. 复现错误2. 分析错误3. 解决问题3.1 下载Hadoop3.2 配置Hadoop3.3 下载winutils3.4 配置winutils 1. 复现错误 今天在运行同事给我的项目&#xff0c;但在项目启动时&#xff0c;报出如下错误&#xff1a; java.io.FileNotFoundException: java.io.FileNotFoundEx…

密码技术 (5) - 数字签名

一. 前言 前面在介绍消息认证码时&#xff0c;我们知道消息认证码虽然可以确认消息的完整性&#xff0c;但是无法防止否认问题。而数字签名可以解决否认的问题&#xff0c;接下来介绍数字签名的原理。 二. 数字签名的原理 数字签名和公钥密码一样&#xff0c;也有公钥和私钥&am…

【分布式事务】

文章目录 解决分布式事务的思路seata四种模式1. XA模式2. AT模式AT模式与XA模式的区别是什么&#xff1f;脏写问题 3. TCC模式事务悬挂和空回滚 4. SAGA模式 四种模式对比口述AT模式与TCC模式高可用 什么是分布式事务&#xff1f; 分布式事务&#xff0c;就是指不是在单个服务或…

JAVA 异常分类及处理

1 概念 如果某个方法不能按照正常的途径完成任务&#xff0c;就可以通过另一种路径退出方法。在这种情况下会抛出一个封装了错误信息的对象。此时&#xff0c;这个方法会立刻退出同时不返回任何值。另外&#xff0c;调用 这个方法的其他代码也无法继续执行&#xff0c;异常处理…

vSAN7.0更换硬盘步骤

更换容量盘 预先检查 查看故障硬盘 清单->集群->监控->vsan->skyline运行->物理磁盘->运维运行状况 检查数据同步状态 清单->集群->监控->vsan->重新同步对象&#xff0c;数值全为0表示未重建。 数据迁移检查 清单->集群->监控->…

ili9431液晶 tft_espi图形库演示 时钟、天气、滚动、气象图标

米思齐tft_spi模块库演示程序。心知天气、阿里云时钟、WiFi信号强度检测、1分钟滚屏、更新天气时间为15分钟、加入天气图标。更新天气次数。断网检测 。此程序为tft_eSPI图形库演示、如感觉好可以自行优化。 ili9431tft_espi库是用于ESP32和ESP8266芯片的TFT LCD驱动程序库&am…

基于Java的厨艺交流平台设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…

初级篇—第一章初识数据库

文章目录 为什么要使用数据库数据库与数据库管理系统数据库的相关概念数据库与数据库管理系统的关系 常用的数据库为什么如此多的厂商要选用MySQL&#xff1f;MySQL的目录 RDBMS 与 非RDBMS关系型数据库(RDBMS)非关系型数据库(非RDBMS) 关系型数据库设计规则表、记录、字段表的…

mathtype如何嵌入到word中?详细mathtype安装步骤教程

mathtype是一款功能特别强大的数学方式编辑软件&#xff0c;为用户提供各种强大的数学公式符号帮助用户进行计算&#xff0c;并且速度很快。有小伙伴知道mathtype如何嵌入到word中吗&#xff0c;这里小编就给大家详细介绍一下mathtype嵌入到word中的方法&#xff0c;有需要的小…

【JVM】第五篇 垃圾收集器G1和ZGC详解

导航 一. G1垃圾收集算法详解1. 大对象Humongous说明2. G1收集器执行一次GC运行的过程步骤3. G1垃圾收集分类4. G1垃圾收集器参数设置5. G1垃圾收集器的优化建议6. 适合使用G1垃圾收集器的场景?二. ZGC垃圾收集器详解1. NUMA与UMA2. 颜色指针3. ZGC的运作过程4. ZGC垃圾收集器…

【源码】hamcrest 源码阅读及空对象模式、模板方法模式的应用

文章目录 前言1. 类图概览2. 源码阅读2.1 抽象类 BaseMatcher2.1 接口 Description提炼模式&#xff1a;空对象模式 2. 接口 Description 与 SelfDescribing 配合使用提炼模式 模板方法 后记 前言 hamcrest &#xff0c;一个被多个测试框架依赖的包。听说 hamcrest 的源码质量…

flutter开发实战-webview插件flutter_inappwebview使用

flutter开发实战-webview插件flutter_inappwebview使用 在开发过程中&#xff0c;经常遇到需要使用WebView&#xff0c;Webview需要调用原生的插件来实现。常见的flutter的webview插件是webview_flutter&#xff0c;flutter_inappwebview。之前整理了一下webview_flutter&…

OpenCV之直线曲线拟合

直线拟合fitLine void fitLine( InputArray points, OutputArray line, int distType,double param, double reps, double aeps ); points:二维点的数组或vector line:输出直线,Vec4f (2d)或Vec6f (3d)的vector distType:距离类型 param:距离参数 reps:径向的精度参数 a…

多线程学习笔记(一)

文章目录 1 线程基础知识复习2 CompletableFuture1、Future和Callable接口2、FutureTask3、对Future的改进4、案例精讲——电商5、常用方法6、CompetableFutureWithThreadPool【重要】 3 锁1、乐观锁和悲观锁2、synchronized 8锁案例3、公平锁和非公平锁4、可重入锁5、死锁及排…