【算法】动态规划

文章目录

  • 概述
  • 背包问题
    • 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.mzph.cn/news/92763.shtml

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

相关文章

【数据结构】AVL树(C++实现)

​ ​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;数据结构 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 上一篇博客&#xff1a;【数据…

c#设计模式-结构型模式 之 享元模式

&#x1f680;简介 享元模式&#xff08;Flyweight Pattern&#xff09;是一种用于性能优化的模式&#xff0c;其核心是运用共享技术来有效支持大量细粒度的对象。享元模式可以避免大量非常相似类的开销。在程序设计中&#xff0c;有时需要生成大量细粒度的类实例来表示数据。…

打开泰坦陨落2找不到msvcp120.dll无法执行代码/msvcr120.dll丢失修复方法

msvcp120.dll 是 Windows 操作系统中的一个动态链接库文件&#xff0c;对于许多程序和游戏的运行起着至关重要的作用。然而&#xff0c;有时候我们可能会遇到 msvcp120.dll 丢失的情况&#xff0c;导致电脑出现各种问题。本文将详细介绍 msvcp120.dll 丢失的四种解决方法&#…

大数据-玩转数据-Flink SQL编程实战 (热门商品TOP N)

一、需求描述 每隔30min 统计最近 1hour的热门商品 top3, 并把统计的结果写入到mysql中。 二、需求分析 1.统计每个商品的点击量, 开窗2.分组窗口分组3.over窗口 三、需求实现 3.1、创建数据源示例 input/UserBehavior.csv 543462,1715,1464116,pv,1511658000 662867,22…

MySQL进阶_3.性能分析工具的使用

文章目录 第一节、数据库服务器的优化步骤第二节、查看系统性能参数第三节、 慢查询日志第四节、 查看 SQL 执行成本第五节、 分析查询语句&#xff1a;EXPLAIN 第一节、数据库服务器的优化步骤 当我们遇到数据库调优问题的时候&#xff0c;可以按照以下流程进行分析。整个流程…

Stm32_标准库_5_呼吸灯_按键控制

Stm32按键和输出差不多 PA1为LED供给正电&#xff0c;PB5放置按键&#xff0c;按键一端接PB5,另一端接负极 void Key_Init(void){RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //APB2总线连接着GPIOBGPIO_InitStructur.GPIO_Mode GPIO_Mode_IPU;GPIO_InitStructur.…

[SpringBoot] 8. aop 获取 request response

最近开发有一个需求需要在 aop 中获取request response &#xff0c;搜索许久没有答案&#xff0c;故此记录&#x1f4dd;&#xff5e; aop 获取 package com.example.easy_im.aop;import com.example.easy_im.Context; import jakarta.servlet.http.HttpServletRequest; impo…

STL学习笔记之容器

首先我们要学习的是容器 第一个是容器的初始化&#xff08;构造方式&#xff09;有三种方式 分别是 第一种 int arr[]{1,2,3} vector<int> v1(arr,arr3) 即容器存放的种类和从另外一个数组去拷贝一段数据。 第二种 vector<int> v2(3,10); 第一个3是指存放…

【Linux进行时】进程地址空间

进程地址空间 例子引入&#xff1a; 我们在讲C语言的时候&#xff0c;老师给大家画过这样的空间布局图&#xff0c;但是我们对它不了解 我们写一个代码来验证Linux进程地址空间 #include<stdio.h> #include<assert.h> #include<unistd.h> int g_value100; …

MATLAB在逐渐被Python淘汰吗?

MATLAB和Python都是非常强大的编程工具&#xff0c;各有优势。以下是两段代码的对比&#xff1a; MATLAB代码&#xff1a; import numpy as np from scipy.linalg import eig# 定义一个矩阵A A np.array([[1, 2], [3, 4]])# 计算矩阵A的特征值和特征向量 D, V eig(A)# 输出…

Java基础---第十二篇

系列文章目录 文章目录 系列文章目录一、获取一个类Class对象的方式有哪些?二、ArrayList 和 LinkedList 的区别有哪些?三、用过 ArrayList 吗?说一下它有什么特点?一、获取一个类Class对象的方式有哪些? 搞清楚类对象和实例对象,但都是对象。 第一种:通过类对象的 get…

[机缘参悟-107] :一个IT人关于顺势而为的思考:顺势而为思想在股-市中的体现与应用

目录 前言&#xff1a; 一、顺势而为的核心思想 1.1 道家“顺势而为”的思想 1.2 佛家“顺势而为”的思想 1.3 儒家“顺势而为”的思想 1.4 无处不在的“势” 1.5 逆势而为的代价 二、顺势而为在股市中的应用 2.1 顺势而为的策略 2.2 在别人疯狂的时候悲观&#xff0…

【C++】多线程的学习笔记——白话文版(bushi

目录 为什么要使用多线程 例子 代码 结果 首先要先学的库——thread库 thread的简介 thread的具体使用方法 基本变量的定义 注意&#xff08;小重点&#xff09; join函数的解读&#xff08;重点&#xff09; detach函数的解读 注意 关于vector和thread是联合使用 …

Mac程序坞美化工具 uBar

uBar是一款为Mac用户设计的任务栏增强软件&#xff0c;它可以为您提供更高效和更个性化的任务管理体验。 以下是uBar的一些主要特点和功能&#xff1a; 更直观的任务管理&#xff1a;uBar改变了Mac上传统的任务栏设计&#xff0c;将所有打开的应用程序以类似于Windows任务栏的方…

学习开发一个RISC-V上的操作系统(汪辰老师) — 环境配置

前言 &#xff08;1&#xff09;此系列文章是跟着汪辰老师的RISC-V课程所记录的学习笔记。 &#xff08;2&#xff09;该课程相关代码gitee链接&#xff1b; &#xff08;3&#xff09;PLCT实验室实习生长期招聘&#xff1a;招聘信息链接 &#xff08;4&#xff09;在学习汪辰老…

计算机竞赛 深度学习疲劳检测 驾驶行为检测 - python opencv cnn

文章目录 0 前言1 课题背景2 相关技术2.1 Dlib人脸识别库2.2 疲劳检测算法2.3 YOLOV5算法 3 效果展示3.1 眨眼3.2 打哈欠3.3 使用手机检测3.4 抽烟检测3.5 喝水检测 4 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; **基于深度学习加…

Guava限流器原理浅析

文章目录 基本知识限流器的类图使用示例 原理解析限流整体流程问题驱动1、限流器创建的时候会初始化令牌吗&#xff1f;2、令牌是如何放到桶里的&#xff1f;3、如果要获取的令牌数大于桶里的令牌数会怎么样4、令牌数量的更新会有并发问题吗 总结 实际工作中难免有限流的场景。…

c++模板小例子

需要注意的是&#xff0c;模板中函数或方法&#xff0c;要在类或头文件中实现。关键字typename 和class基本等同。构造类模板时&#xff0c;要指明模板参数类型&#xff0c;而函数模板则不用指明参数类型。 #pragma once#include <string.h>#include <windows.h>us…

2023/9/27 -- ARM

【汇编语言相关语法】 1.汇编语言的组成部分 1.伪操作&#xff1a;不参与程序的执行&#xff0c;但是用于告诉编译器程序该怎么编译 .text .global .end .if .else .endif .data2.汇编指令 编译器将一条汇编指令编译成一条机器码&#xff0c;在内存里一条指令占4字节内…

JavaWeb 学习笔记 10:Element

JavaWeb 学习笔记 10&#xff1a;Element Element 是一个基于 Vue 的前端组件框架&#xff0c;使用它可以快速构建美观的前端页面。 1.快速开始 创建一个简单的 JavaWeb 应用。 添加一个 Html 页面&#xff0c;并在<head>标签中加入 Element 和 Vue 的相关 js 引用&a…