acwing算法基础之动态规划--背包问题

目录

  • 1 基础知识
  • 2 模板
  • 3 工程化

1 基础知识

(零)
背包问题描述:有 N N N个物品,每个物品的体积是 v i v_i vi,价值是 w i w_i wi,现有容量是 V V V的背包,求这个背包能装下的物品的最大价值。

01背包问题:每个物品只有1个。
完全背包问题:每个物品有无穷多个。
多重背包问题:第 i i i个物品有 s i s_i si个。
分组背包问题:有N组物品,每组有 s i s_i si个物品,但只能选择其中一个。

(一)
01背包问题讲解。

状态定义f[i][j]:从前 i i i个物品中选择总体积不超过 j j j的物品的总价值的最大值。

状态转移:

  1. 不选择第 i i i个物品,即从前 i − 1 i-1 i1个物品中选择总体积不超过 j j j的物品,根据状态的定义可知,为f[i-1][j]
  2. 选择第 i i i个物品,即从前 i − 1 i-1 i1个物品中选择总体积不超过 j − v [ i ] j-v[i] jv[i]的物品,然后再加上w[i],即f[i-1][j - v[i]] + w[i]

f[i][j]的值取上述两者中的最大值即可。

初始化:f[0][0~V]=0

最终答案:f[N][V]

用代码表示如下,

#include <iostream>using namespace std;const int N = 1010;
int n, m;
int v[N];
int w[N];
int f[N][N];int main() {cin >> n >> m;for (int i = 1; i <= n; ++i) cin >> v[i] >> w[i];for (int i = 1; i <= n; ++i) {for (int j = 0; j <= m; ++j) {f[i][j] = f[i-1][j];if (j >= v[i]) f[i][j] = max(f[i][j], f[i-1][j - v[i]] + w[i]);}}cout << f[n][m] << endl;return 0;
}

考虑到上述状态转移过程中,在计算第i层的状态时,只用到了第i-1层的信息,故可以使用滚动数组进行优化,将状态表示降成一维的。可以用一个临时数组来存取上一层的状态。更进一步,考虑f[i][j] = max(f[i][j], f[i-1][j - v[i]] + w[i]),发现计算f[i][j]时,需要知道f[i-1][j-v[i]]的信息,而我们可以从j=mj=v[i]的顺序进行更新,那么使用的就是上一层的f[j-v[i]],故省去了 O ( N ) O(N) O(N)的临时数组的空间。

C++代码如下,

#include <iostream>using namespace std;const int N = 1010;
int n, m;
int v[N];
int w[N];
int f[N];int main() {cin >> n >> m;for (int i = 1; i <= n; ++i) cin >> v[i] >> w[i];for (int i = 1; i <= n; ++i) {for (int j = m; j >= v[i]; --j) {f[j] = max(f[j], f[j - v[i]] + w[i]);}}cout << f[m] << endl;return 0;
}

(二)
完全背包问题讲解。

状态定义同上,即f[i][j]:从前 i i i个物品中选择总体积不超过 j j j的物品的总价值的最大值。

状态转移稍有不同,如下,

  1. 不选第 i i i个物品,即f[i-1][j]
  2. 选1个第 i i i个物品,即f[i-1][j - v[i]] + w[i]
  3. 选2个第 i i i个物品,即f[i-1][j - v[i] * 2] + w[i] * 2
    ……
  4. k k k个第 i i i个物品,即f[i-1][j - v[i] * k] + w[i] * k

初始化:f[0][0~V]=0

最终答案:f[N][V]

C++代码如下,

#include <iostream>using namespace std;const int N = 1010;
int n, m;
int v[N], w[N];
int f[N][N];int main() {cin >> n >> m;for (int i = 1; i <= n; ++i) cin >> v[i] >> w[i];for (int i = 1; i <= n; ++i) {for (int j = 0; j <= m; ++j) {for (int k = 0; k * v[i] <= j; ++k) {f[i][j] = max(f[i][j], f[i-1][j - v[i] * k] + w[i] * k);}}}cout << f[n][m] << endl;return 0;
}

上述代码的三重循环可以优化到两重循环,考虑状态f[i][j]的求解,
f [ i ] [ j ] = m a x ( f [ i − 1 ] [ j ] , f [ i − 1 ] [ j − v [ i ] ] + w [ i ] , f [ i − 1 ] [ j − v [ i ] ∗ 2 ] + w [ i ] ∗ 2 , ⋯ , f [ i − 1 ] [ j − v [ i ] ∗ k ] + w [ i ] ∗ k ) f[i][j] = max(f[i-1][j], \\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ f[i-1][j -v[i]] + w[i], \\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ f[i-1][j - v[i] * 2] + w[i] * 2,\\ \ \ \ \ \ \ \ \ \ \ \ \ \ \cdots,\\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ f[i-1][j-v[i] * k] + w[i] * k) f[i][j]=max(f[i1][j],                                              f[i1][jv[i]]+w[i],                                                         f[i1][jv[i]2]+w[i]2,             ,                                                         f[i1][jv[i]k]+w[i]k)
考虑状态f[i][j - v[i]]的求解,
f [ i ] [ j − v [ i ] ] = m a x ( f [ i − 1 ] [ j − v [ i ] ] , f [ i − 1 ] [ j − v [ i ] ∗ 2 ] + w [ i ] , f [ i − 1 ] [ j − v [ i ] ∗ 3 ] + w [ i ] ∗ 2 , ⋯ , f [ i − 1 ] [ j − v [ i ] ∗ k ] + w [ i ] ∗ k ) f[i][j - v[i]] = max(f[i-1][j- v[i]], \\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ f[i-1][j -v[i] * 2] + w[i], \\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ f[i-1][j - v[i] * 3] + w[i] * 2,\\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \cdots,\\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ f[i-1][j-v[i] * k] + w[i] * k) f[i][jv[i]]=max(f[i1][jv[i]],                                                    f[i1][jv[i]2]+w[i],                                                          f[i1][jv[i]3]+w[i]2,              ,                                                          f[i1][jv[i]k]+w[i]k)
观察上式,发现有,
f [ i ] [ j ] = m a x ( f [ i − 1 ] [ j ] , f [ i ] [ j − v [ i ] ] + w [ i ] ) f[i][j] = max(f[i-1][j], \\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ f[i][j-v[i]] + w[i]) f[i][j]=max(f[i1][j],                                        f[i][jv[i]]+w[i])
故写成代码如下,

#include <iostream>using namespace std;const int N = 1010;
int n, m;
int v[N], w[N];
int f[N][N];int main() {cin >> n >> m;for (int i = 1; i <= n; ++i) cin >> v[i] >> w[i];for (int i = 1; i <= n; ++i) {for (int j = 0; j <= m; ++j) {f[i][j] = f[i-1][j];if (j >= v[i]) f[i][j] = max(f[i][j], f[i][j - v[i]] + w[i]);}}cout << f[n][m] << endl;return 0;
}

更进一步,可以利用滚动数组进行优化,考虑状态f[i][j]的计算公式f[i][j] = max(f[i][j], f[i][j - v[i]] + w[i]),由于此处使用的是第i层的j-v[i],故无需将jj=mj=v[i]这样倒序遍历,正序遍历即可。

C++代码如下,

#include <iostream>using namespace std;const int N = 1010;
int n, m;
int v[N], w[N];
int f[N];int main() {cin >> n >> m;for (int i = 1; i <= n; ++i) cin >> v[i] >> w[i];for (int i = 1; i <= n; ++i) {for (int j = 0; j <= m; ++j) {if (j >= v[i]) f[j] = max(f[j], f[j - v[i]] + w[i]);}}cout << f[m] << endl;return 0;
}

(三)
多重背包问题讲解。

状态定义同(一),即f[i][j]:使用前 i i i个物体且总体积不超过 j j j,总价值的最大值。
状态转移:

  1. 不选择第 i i i个物品,即f[i-1][j]
  2. 选择1个第 i i i个物品,即f[i-1][j-v[i]] + w[i]
  3. 选择2个第 i i i个物品,即f[i-1][j-v[i]*2] + w[i]*2
    ……
  4. 选择s[i]个第 i i i个物品,即f[i-1][j-v[i]*s[i]] + w[i] * s[i]

C++代码如下,

#include <iostream>using namespace std;const int N = 110;
int n, m;
int v[N], w[N], s[N];
int f[N][N];int main() {cin >> n >> m;for (int i = 1; i <= n; ++i) cin >> v[i] >> w[i] >> s[i];for (int i = 1; i <= n; ++i) {for (int j = 0; j <= m; ++j) {for (int k = 0; k * v[i] <= j && k <= s[i]; ++k) {f[i][j] = max(f[i][j], f[i-1][j - v[i] * k] + w[i] * k);}}}cout << f[n][m] << endl;return 0;
}

上述时间复杂度为 O ( n ⋅ m ⋅ s ) O(n\cdot m\cdot s) O(nms),可以优化到 O ( n ⋅ m ⋅ l o g ( s ) ) O(n\cdot m \cdot log(s)) O(nmlog(s))

具体介绍如下,考虑到有s[i]个第 i i i类物品,可以将其拆成 l o g ( s [ i ] ) log(s[i]) log(s[i])个,比如有20个第 i i i类物品,可以拆成1个、2个、4个、8个和5个。然后转换成01背包问题。

C++代码如下,

#include <iostream>using namespace std;const int N = 25000, M = 2010;
int n, m, cnt;
int v[N], w[N];
int f[M];int main() {cin >> n >> m;for (int i = 1; i <= n; ++i) {int a, b, c;cin >> a >> b >> c;//体积a,价值b,个数cint k = 1;while (c >= k) {cnt ++;v[cnt] = k * a;w[cnt] = k * b;c -= k;k <<= 1;}if (c > 0) {cnt++;v[cnt] = c * a;w[cnt] = c * b;}}//做一遍01背包问题for (int i = 1; i <= cnt; ++i) {for (int j = m; j >= v[i]; --j) {f[j] = max(f[j], f[j - v[i]] + w[i]);}}cout << f[m] << endl;return 0;
}

(四)
分组背包问题讲解。

N N N组物品,每组物品有 s i s_i si类,每一类只有一个,分别有体积 v i v_i vi、价值 w i w_i wi,现在有体积为 V V V的背包,只能从每组物品中挑选一个或者不选,求背包的最大价值。

状态定义基本同(一),即f[i][j]:从前 i i i个物品中选择总体积不超过 j j j的,其最大价值。

状态转移,有:

  1. 不选择第 i i i组物品,f[i-1][j]
  2. 选择第 i i i组物品中的第0个,即f[i-1][j - v[i][0]] + w[i][0]
  3. 选择第 i i i组物品中的第1个,即f[i-1][j - v[i][1]] + w[i][1]
    ……
  4. 选择第 i i i组物品中的第k个,即f[i-1][j - v[i][k]] + w[i][k]

C++代码如下,

#include <iostream>using namespace std;const int N = 110;
int n, m;
int s[N];
int v[N][N], w[N][N];
int f[N];int main() {cin >> n >> m;for (int i = 1; i <= n; ++i) {cin >> s[i];for (int j = 0; j < s[i]; ++j) {cin >> v[i][j] >> w[i][j];}}for (int i = 1; i <= n; ++i) {for (int j = m; j >= 0; --j) {for (int k = 0; k < s[i]; ++k) {if (v[i][k] <= j) f[j] = max(f[j], f[j - v[i][k]] + w[i][k]);}}}cout << f[m] << endl;return 0;
}

2 模板

暂无。。。

3 工程化

暂无。。。

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

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

相关文章

C#中的警告CS0120、CS0176、CS0183、CS0618、CS0649、CS8600、CS8601、CS8602、CS8604、CS8625及处理

目录 一、CS0120 二、CS0176 1.解决前 2.解决后 3.解决办法 三、CS0183 四、CS0618 五、CS8600 六、CS8602 七、CS8622 1. 解决前&#xff1a; 2. 解决后&#xff1a; 3.解决方法&#xff1a; 八、CS8604和CS8625 九、CS0649 十、CS8601 一、CS0120 严重性 代…

【算法萌新闯力扣】:回文链表

力扣题目&#xff1a;回文链表 开篇 今天是备战蓝桥杯的第23天。我加入的编程导航算法通关村也在今天开营啦&#xff01;那从现在起&#xff0c;我的算法题更新会按照算法村的给的路线更新&#xff0c;更加系统。大家也可以关注我新开的专栏“算法通关村”。里面会有更全面的知…

pandas教程:Techniques for Method Chaining 方法链接的技巧

文章目录 12.3 Techniques for Method Chaining&#xff08;方法链接的技巧&#xff09;1 The pipe Method&#xff08;pipe方法&#xff09; 12.3 Techniques for Method Chaining&#xff08;方法链接的技巧&#xff09; 对序列进行转换的时候&#xff0c;我们会发现会创建很…

操作系统的中断与异常(408常考点)

为了进行核心态和用户态两种状态的切换&#xff0c;引入了中断机制。 中断是计算机系统中的一种事件&#xff0c;它会打断CPU当前正在执行的程序&#xff0c;转而执行另一个程序或者执行特定的处理程序。中断可以来自外部设备&#xff08;如键盘、鼠标、网络等&#xff09;、软…

1072 Gas Station (最短路径同时求最短路,最长路,总路径)

题意&#xff1a;给定几处居民住所与几处预选加油站点&#xff0c;求离最近的居民住所最远且所有居民都在该站点服务区内的加油站点&#xff0c;如果有多个&#xff0c;则选择平均距离最小的&#xff0c;再有多个&#xff0c;选择序号最小的。 思路&#xff1a;刚开始不知道未…

振南技术干货集:FFT 你知道?那数字相敏检波 DPSD 呢?(1)

注解目录 1 、DPSD 的基础知识 1.1 应用模型 1.2 原理推导 1.3 硬件 PSD &#xff08;相敏检波&#xff0c;就是从繁乱复杂的信号中将我们关心的信号检出来&#xff0c;同时对相位敏感。 数学原理&#xff0c;逃不掉的&#xff0c;硬着头皮看吧。&#xff09; 2 、DPSD …

【电路笔记】-电阻器颜色代码与阻值计算

电阻器颜色代码与阻值计算 文章目录 电阻器颜色代码与阻值计算1、概述2、计算电阻器颜色代码值3、贴片电阻器 电阻器颜色编码使用色带轻松识别电阻器的电阻值及其百分比容差。 1、概述 由于有许多不同类型的电阻器可用&#xff0c;我们需要形成电阻器颜色代码系统以便能够识别…

LuatOS-SOC接口文档(air780E)--repl - “读取-求值-输出” 循环

示例 --[[ 本功能支持的模块及对应的端口 模块/芯片 端口 波特率及其他参数 Air101/Air103 UART0 921600 8 None 1 Air105 UART0 1500000 8 None 1 ESP32C3 UART0 921600 8 None 1 -- 注意, 简约版(无CH343)不支持 ESP32C2 …

Java 注解在 Android 中的使用场景

Java 元注解有 5 种&#xff0c;常用的是 Target 和 Retention 两个。 其中 Retention 表示保留级别&#xff0c;有三种&#xff1a; RetentionPolicy.SOURCE - 标记的注解仅保留在源码级别中&#xff0c;并被编译器忽略RetentionPolicy.CLASS - 标记的注解在编译时由编译器保…

力扣104. 二叉树的最大深度

目录 1.解题思路2.代码实现 1.解题思路 如果我们知道了左子树和右子树的最大深度&#xff0c;那么该二叉树的最大深度即为大的深度加一,而左子树和右子树的最大深度又可以以同样的方式进行计算。因此我们可以用「深度优先搜索」的方法来计算二叉树的最大深度。具体而言&#x…

Vue框架学习笔记——事件scroll和wheel的区别

文章目录 前文提要滚动条滚动事件 scroll鼠标滚动事件 wheel二者不同点 前文提要 本人仅做个人学习记录&#xff0c;如有错误&#xff0c;请多包涵 滚动条滚动事件 scroll scroll事件绑定html页面中的指定滚动条&#xff0c;无论你拖拽滚动条&#xff0c;选中滚动条之后按键盘…

Doris的PROPERTIES与ENGINE(九)

接上篇----------Doris分区与分桶 在建表语句的最后 PROPERTIES 中&#xff0c;可以指定以下两个参数&#xff1a; replication_num 每个 Tablet 的副本数量。默认为 3&#xff0c;建议保持默认即可。在建表语句中&#xff0c;所有 Partition 中的 Tablet 副本数量统一指定。…

【论文阅读】TACAN:控制器局域网中通过隐蔽通道的发送器认证

文章目录 摘要一、引言二、相关工作三、系统和对手模型3.1 系统模型对手模型 四、TACAN4.1 TACAN 架构4.2 发送方认证协议4.3 基于IAT的隐蔽通道4.4 基于偏移的隐蔽通道&#xff08;本节公式格式暂未整理&#xff09;4.5 基于LSB的隐蔽通道 摘要 如今&#xff0c;汽车系统与现…

一个GPU版本的遗传算法迭代xgboost最优参数的示例,这里用的是自定义损失函数

一个简单的遗传算法迭代xgboost最优参数的示例&#xff0c;这里用的是自定义损失函数 import pandas as pd import numpy as np import xgboost as xgb from sko.GA import GA from sklearn.model_selection import train_test_split from sklearn.linear_model import Logisti…

vscode Markdown 预览样式美化多方案推荐

优雅的使用 vscode写 Markdown&#xff0c;预览样式美化 1 介绍 我已经习惯使用 vscode 写 markdown。不是很喜欢他的 markdown 样式&#xff0c;尤其是代码块高亮的样式。当然用 vscode 大家基本上都会选择安装一个Markdown-preview-enhanced的插件&#xff0c;这个插件的确…

SpringBoot定时任务报错Unexpected error occurred in scheduled task原因及其解决方法(亲测有效)

问题 spring boot项目在线上一直正常运行没有错误&#xff0c;然后今天发生了报错&#xff0c;如图 这是一个定时器错误&#xff0c;发生这个报错 主要有两个原因 定时器编写的有错误Scheduled注解方式级别高于资源注入级别&#xff0c;导致了资源注入失败 以下是我的代码 …

单片机学习4——中断的概念

中断的概念&#xff1a; CPU在处理A事件的时候&#xff0c;发生了B事件&#xff0c;请求CPU迅速去处理。&#xff08;中断产生&#xff09; CPU暂时中断当前的工作&#xff0c;转去处理B事件。&#xff08;中断响应和中断服务&#xff09; 待CPU将B事件处理完毕后&#xff0…

【物联网开发】、【小程序蓝牙通讯数据校验】JS CRC-16-MODBUS 验证 高位在前地位在后;JS异或校验;16进制字符串和float互转

1.CRC校验 /*计算CRC-16/MODBUS校验位高低位*/ function calculateCRC16Modbus(dataHexString) {const dataBytes [];for (let i 0; i < dataHexString.length; i 2) {dataBytes.push(parseInt(dataHexString.substr(i, 2), 16));}let crc 0xFFFF;const polynomial 0x…

【数据结构初阶(5)】链式队列的基本操作实现

文章目录 队列的定义初始化队列队尾入队列队头出队列取队头元素取队尾元素获取队列有效元素个数判断队空销毁队列 因为队列比较简单&#xff0c;关于队列的概念就不过多赘述了&#xff0c;本文只讲链队的基本操作实现 队列的定义 定义队列结点结构 链队中的每个结点都应该包…

windows安装mysql5.7.26

解压mysql5.7.26文件夹拷贝到c:下 添加系统环境变量C:\mysql-5.7.26\bin cmd管理员打开终端,进入C:\mysql-5.7.26\bin 运行mysqld --initialize&#xff0c;生成data目录的文件。 在安装目录下创建my.ini文件&#xff0c;点击编辑&#xff0c;配置以下信息&#xff1a; 设置my…