算法学习之动态规划DP——背包问题

一、01背包问题

(一)题目

有 N 件物品和一个容量是 V的背包。每件物品只能使用一次。

第i件物品的体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

输入格式

第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。

接下来有 N行,每行两个整数 vi,wi,用空格隔开,分别表示第i件物品的体积和价值。

输出格式

输出一个整数,表示最大价值。

数据范围

0<N,V≤1000
0<vi,wi≤1000

输入样例

4 5
1 2
2 4
3 4
4 5

输出样例:

8

(二 )题解

 【一】 二维动态规划

(1)状态f[i][j]定义:前 i 个物品,背包容量 j下的最优解(最大价值):

    当前的状态依赖于之前的状态,可以理解为从初始状态f[0][0] = 0开始决策,有 N

件物品,则需要 N 次决 策,每一次对第 i件物品的决策,状态f[i][j]不断由之前的状态更新而来。

(2)当前背包容量不够(j < v[i]),没得选,因此前 i个物品最优解即为前 i−1个物品最优解:

    对应代码:f[i][j] = f[i - 1][j]。

(3)当前背包容量够,可以选,因此需要决策选与不选第i个物品:

    选:f[i][j] = f[i - 1][j - v[i]] + w[i]。
    不选:f[i][j] = f[i - 1][j] 。
    我们的决策是如何取到最大价值,因此以上两种情况取 max() 。

对应代码

#include<iostream>
#include<cstdio>
#include<cstring>using namespace std;const int N = 1010;int v[N], w[N];
int f[N][N];int n, m;int main(){scanf("%d%d", &n, &m);for(int i = 1; i <= n; ++ i) scanf("%d%d", &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]);}}printf("%d", f[n][m]);return 0;
}
【二】优化:状态压缩(二维变一维)

将状态f[i][j]优化到一维f[j],实际上只需要做一个等价变形。

为什么可以这样变形呢?我们定义的状态f[i][j]可以求得任意合法的i与j最优解,但题目只需要求得最终状态f[n][m],因此我们只需要一维的空间来更新状态。

(1)状态f[j]定义:N件物品,背包容量j下的最优解。

(2)注意枚举背包容量j必须从m开始

为什么一维情况下枚举背包容量需要逆序?在二维情况下,状态f[i][j]是由上一轮i - 1的状态得来的,f[i][j]与f[i - 1][j]是独立的。而优化到一维后,如果我们还是正序,则有f[较小体积]更新到f[较大体积],则有可能本应该用第i-1轮的状态却用的是第i轮的状态。

例如,一维状态第i轮对体积为 3的物品进行决策,则f[7]由f[4]更新而来,这里的f[4]正确应该f[i-1][4],但从小到大枚举j这里的f[4]在第i轮计算却变成了f[i][4]。当逆序枚举背包容量j时,我们求f[7]同样由f[4]更新,但由于是逆序,这里的f[4]还没有在第i轮计算,所以此时实际计算的f[4]仍然是f[i - 1][4]。

简单来说,一维情况正序更新状态f[j]需要用到前面计算的状态已经被「污染」,逆序则不会有这样的问题。

状态转移方程为:f[j] = max(f[j], f[j - v[i]] + w[i] 。

tips:若通过减少维数来进行状态压缩,要注意是否有一维循环需要逆序来保证更新所用到的状态没有被污染。

对应代码

#include<iostream>
#include<cstdio>
#include<cstring>using namespace std;const int N = 1010;int v[N], w[N];
int f[N];int n, m;int main(){scanf("%d%d", &n, &m);for(int i = 1; i <= n; ++ i) scanf("%d%d", &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]);}}printf("%d", f[m]);return 0;
}

二、完全背包

(一)题目

有 N 种物品和一个容量是V的背包,每种物品都有无限件可用。

第i种物品的体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

输入格式

第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。

接下来有N行,每行两个整数 vi,wi,用空格隔开,分别表示第i种物品的体积和价值。

输出格式

输出一个整数,表示最大价值。

数据范围

0<N,V≤1000

0<vi,wi≤1000

输入样例

4 5
1 2
2 4
3 4
4 5

输出样例:

10

(二)题解

闫式DP分析法

对应代码

二维DP

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>using namespace std;const int N = 1010;int v[N], w[N];
int f[N][N];int n, m;int main(){scanf("%d%d", &n, &m);for(int i = 1; i <= n; ++ i) scanf("%d%d", &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]);        }}printf("%d", f[n][m]);return 0;
}

一维DP

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>using namespace std;const int N = 1010;int v[N], w[N];
int f[N];int n, m;int main(){scanf("%d%d", &n, &m);for(int i = 1; i <= n; ++ i) scanf("%d%d", &v[i], &w[i]);for(int i = 1; i <= n; ++ i){for(int j = 0; j <= m; ++ j){f[j] = f[j];  // 等价替换f[i][j] = f[i-1][j];if(j >= v[i]) f[j] = max(f[j], f[j-v[i]]+w[i]);  // 等价替换f[i][j] = max(f[i][j], f[i][j-v[i]]+w[i])// 总结:替换的是f[i-1][...]还是f[i][...]取决于在该i层循环中,等号右边的数组中出现的下标有没有更新过,若更新过就是f[i][...], 反之则是f[i-1][...]}}printf("%d", f[m]);return 0;
}

简化

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>using namespace std;const int N = 1010;int v[N], w[N];
int f[N];int n, m;int main(){scanf("%d%d", &n, &m);for(int i = 1; i <= n; ++ i) scanf("%d%d", &v[i], &w[i]);for(int i = 1; i <= n; ++ i){for(int j = v[i]; j <= m; ++ j){f[j] = max(f[j], f[j-v[i]]+w[i]);      }}printf("%d", f[m]);return 0;
}

 三、多重背包问题

(一)题目

有N 种物品和一个容量是V的背包。

第i种物品最多有 si 件,每件体积是vi,价值是wi。

求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。

输入格式

第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。

接下来有N行,每行三个整数 vi,wi,si,用空格隔开,分别表示第i种物品的体积、价值和数量。

输出格式

输出一个整数,表示最大价值。

数据范围

0<N≤1000
0<V≤2000
0<vi,wi,si≤2000

输入样例

4 5
1 2 3
2 4 1
3 4 3
4 5 2

输出样例

10

 

(二)题解

思路:可以将多重背包问题转化为01背包问题。

1.朴素的转化思路

可以直接转化为有s[1]+s[2]+...s[n]个物品,背包容量为V的01背包问题。

对应代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>using namespace std;const int N = 110;int n, m;int f[N];int main(){scanf("%d%d", &n, &m);for(int i = 1; i <= n; ++ i){int v, w, s;scanf("%d%d%d", &v, &w, &s);for(int j = m; j >= 0; -- j){for(int k = 1; k <= s; ++ k){if(j >= k*v) f[j] = max(f[j], f[j-k*v]+k*w);}}}printf("%d", f[m]);return 0;
}

时间复杂度分析

O(N*V*Si)(大约为10^9)在本题目的数据范围限制下会超时。

2.优化

上述做法是将Si拆成了Si个1。

tips:

能否将Si拆成小于Si个数,使这些数可以表示1~Si之间的所有数?

答案是可以的。

(结论)其实只需要将Si拆成log(Si)(上取整)个数即可。这Si个数分别为2^0, 2^1, 2^2……2^log(Si)。

注意:对于Si恰好等于以2为底的指数减1倍时,是恰好符合题意的。而其他值在经过log(Si)(上取整)个数相加后仍会小于Si,只需要将剩余的这部分单独拿出来即可。

对应代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>using namespace std;const int N = 2010;int n, m;int f[N];struct good{int v, w;
};int main(){vector<good> goods;scanf("%d%d", &n, &m);for(int i = 0; i < n; ++ i){int v, w, s;scanf("%d%d%d", &v, &w, &s);for(int k = 1; k <= s; k *= 2){s -= k;goods.push_back({k*v, k*w});}if(s > 0) goods.push_back({s*v, s*w});}for(auto item: goods){for(int j = m; j >= item.v; -- j){f[j] = max(f[j], f[j-item.v]+item.w);}}printf("%d", f[m]);return 0;
}

四、分组背包问题

(一)题目

有 N 组物品和一个容量是V的背包。

每组物品有若干个,同一组内的物品最多只能选一个。
每件物品的体积是 vij,价值是 wij,其中 i 是组号,j是组内编号。

求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。

输出最大价值。

输入格式

第一行有两个整数 N,V,用空格隔开,分别表示物品组数和背包容量。

接下来有N组数据:

  • 每组数据第一行有一个整数 S,表示第i个物品组的物品数量;
  • 每组数据接下来有 S行,每行有两个整数 vij,wij,用空格隔开,分别表示第 i 个物品组的第j个物品的体积和价值;

输出格式

输出一个整数,表示最大价值。

数据范围

0<N,V≤100

0<Si≤100
0<vij,wij≤100

输入样例

3 5
2
1 2
2 4
1
3 4
1
4 5

输出样例

8

(二)题解

注意:多重背包问题是分组背包问题的一个特殊情况。

分组背包问题同样也是01背包问题的一个变种。

对应代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>using namespace std;const int N = 110;int n, m;int v[N], w[N];
int f[N];int main(){scanf("%d%d", &n, &m);for(int i = 0; i < n; ++ i){int s;scanf("%d", &s);for(int j = 1; j <= s; ++ j) scanf("%d%d", &v[j], &w[j]);for(int j = m; j >= 0; -- j){for(int k = 1; k <= s; ++ k){if(j >= v[k]) f[j] = max(f[j], f[j-v[k]]+w[k]);}}}printf("%d", f[m]);return 0;
}

总结

01背包问题,多重背包问题,分组背包问题是同一大类背包问题。在i层循环中只能做一个选择(即选与不选)(对于多重背包和分组背包的选对应的是多种选择)。

而完全背包是另一大类问题。

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

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

相关文章

牛客周赛 Round 36 解题报告 | 珂学家 | 状态DP + 构造 + 9棵树状数组

前言 整体评价 今天相对容易&#xff0c;E的构造题&#xff0c;感谢出题人极其善意的Case 1, 算是放水了。F题是个很典的结论题&#xff0c;由于存在动态点修改&#xff0c;所以引入树状数组做区间和的快速计算。 A. 小红的数位删除 题型: 签到 s input()print (s[:-3])B. …

状态机高阶讲解-02

261 00:11:22,483 --> 00:11:25,260 或依赖于这个&#xff0c;在这里表达 262 00:11:26,780 --> 00:11:30,000 Moore是说什么&#xff0c;在这里表达 263 00:11:30,280 --> 00:11:30,523 264 00:11:30,523 --> 00:11:33,443 在状态里表达&#xff0c;状态的什么 …

【Python】新手入门:全局变量和局部变量的概念、区别以及用法

【Python】新手入门&#xff1a;全局变量和局部变量的概念、区别以及用法 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&#x1f448;…

ABC 344

ABC 344 ABC用python写代码会比较快 A 题可以积累简洁的写法&#xff1a; S input() a, b, c S.split(|) print(ac)#include<bits/stdc.h> using namespace std; int main(){string s;cin >> s;int x s.find("|"); // The first occurrence of | w…

登录凭证------

为什么需要登录凭证&#xff1f; web开发中&#xff0c;我们使用的协议http是无状态协议&#xff0c;http每次请求都是一个单独的请求&#xff0c;和之前的请求没有关系&#xff0c;服务器就不知道上一步你做了什么操作&#xff0c;我们需要一个办法证明我没登录过 制作登录凭…

有源电桥电路

有源电桥电路 有源电桥由A3运放的正向输入端与负向输入端电压相等且为零可知&#xff1a;G点&#xff08;待测阻抗Zx与被测阻抗Rs的连接点&#xff09;电平一直为零&#xff0c;也就是平衡点虚地点&#xff0c;Ux与Us也就变成参照虚地点的绝对相量电压。并且根据运放的虚断原理…

FFmpeg——开源的开源的跨平台音视频处理框架简介

引言&#xff1a; FFmpeg是一个开源的跨平台音视频处理框架&#xff0c;可以处理多种音视频格式。它由Fabrice Bellard于2000年创建&#xff0c;最初是一个只包括解码器的项目。后来&#xff0c;很多开发者参与其中&#xff0c;为FFmpeg增加了多种新的功能&#xff0c;例如编码…

U盘秒变“零字节”?数据恢复全攻略!

一、遭遇U盘“零字节”危机 在数字化时代的浪潮中&#xff0c;U盘凭借其便携性和大容量&#xff0c;早已成为我们工作和生活中不可或缺的数据存储工具。然而&#xff0c;有时我们可能会突然遭遇一个令人头疼的问题——U盘显示0字节。明明前一天还存满了重要的文件&#xff0c;…

Android视角看鸿蒙第四课(module.json中的各字段含义之descriptionmainElement)修改程序入口

Android视角看鸿蒙第三课(module.json中的各字段含义之description&mainElement) 前言 上编文章了解了module.json中的name和type两个字段的含义及变更字段需要注意的事项&#xff0c;也明白了如何去实现类似Android library的功能。 这篇文章继续了解module.json中的des…

指针总结及例题总结

1 定义 指针是用来存放地址的变量 不同类型的指针变量所占用的存储空间是相同的&#xff0c;sizeof(int)sizeof(char)sizeof(double)... *是解引用操作符&#xff0c;&是取地址操作符&#xff0c;两者有着抵消作用 int a20;int* p&a;*p*&a20; 2&#xff0c;…

【C++11】包装器和bind

文章目录 一. 为什么要有包装器&#xff1f;二. 什么是包装器&#xff1f;三. 包装器的使用四. bind 函数模板1. 为什么要有 bind &#xff1f;2. 什么是 bind ?3. bind 的使用场景 一. 为什么要有包装器&#xff1f; function 包装器&#xff0c;也叫作适配器。C 中的 funct…

Vue.js计算属性:实现数据驱动的利器

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

RESTful API学习

RESTful API REST&#xff08;英文&#xff1a;Representational State Transfer&#xff0c;简称REST&#xff0c;直译过来表现层状态转换&#xff09;是一种软件架构风格、设计风格&#xff0c;而不是标准&#xff0c;只是提供了一组设计原则和约束条件。它主要用于客户端和…

光伏数字化管理平台:驱动绿色能源革命的智能化引擎

随着全球对可再生能源需求的不断增长&#xff0c;光伏产业已经成为推动绿色能源革命的重要力量。在这个背景下&#xff0c;光伏数字化管理平台应运而生&#xff0c;以其强大的数据处理、实时监控和智能优化功能&#xff0c;为光伏电站的运营管理和维护带来了革命性的变革。 光伏…

储能系统--户用储能美洲市场(三)

2、美洲市场 2.1、美国户储发展驱动力 &#xff08;1&#xff09;电网老化带来配储需求&#xff0c;户用光储成家庭第二用电保障 美国大部分电网建于20世纪60和70年代&#xff0c;超70%以上的输电系统已经超过了25年&#xff0c;在高负荷运转或者外部环境承压时&#xff0c;…

深入理解Hive:探索不同的表类型及其应用场景

文章目录 1. 引言2. Hive表类型概览2.1 按照数据存储位置2.2 按照数据管理方式2.3 按照查询优化2.4 按照数据的临时性和持久性 3. 写在最后 1. 引言 在大数据时代&#xff0c;Hive作为一种数据仓库工具&#xff0c;为我们提供了强大的数据存储和查询能力。了解Hive的不同表类型…

【数学建模】层次分析

1.建立递阶层次结构模型 2.构造出各层次中的所有判断矩阵 对指标的重要性进行两两比较&#xff0c;构造判断矩阵&#xff0c;科学求出权重 矩阵中元素aij的意义是&#xff0c;第i个指标相对第j个指标的重要程度 对角线1&#xff0c;aijaji1 矛盾——>一致性检验

网络安全:OpenEuler 部署 jumpserver 堡垒机

目录 一、实验 1.环境 2.OpenEuler 部署 jumpserver 堡垒机 3.OpenEuler 使用 jumpserver 堡垒机&#xff08;管理Linux&#xff09; 4.OpenEuler 使用 jumpserver 堡垒机&#xff08;管理Windows&#xff09; 二、问题 1.jumpserver 安装报错 一、实验 1.环境 &#x…

【❤️算法笔记❤️】-每日一刷-21、合并两个有序链表

文章目录 题目思路解答 题目 简单 相关标签 相关企业 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4]示例 2&#xff1a; 输入…

【Linux】shell理解及linux权限解读(“花花公子Root”的自由人生)

目录 1.shell外壳理解 1.1 什么是shell外壳&#xff1a; 1.2 为什么存在shell外壳程序&#xff1a; 1.3外壳程序的具体工作阶段是怎么样的&#xff1f;&#xff08;招实习生&#xff0c;工作失败也不影响公司&#xff09; 2.linux下的权限的概念 2.1linux的用户 2.2.文件类型和…