问题描述:

给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最高。

分类:

有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。 第 i件物品的体积是 v_i,价值是 w_i。求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。输出最大价值。

问题变形
完全背包:每种物品都有无限多个可用;
多重背包:每种物品有s[i] 个可用;
分组背包:物品有 N组,每一组里面只能选一个物品。
01背包问题 完全背包问题 多重背包问题 ,恰好装满、求方案总数、求所有的方案等

知识点important

dp(动态规划)&&dfs(深度优先)

01背包问题:

一共有N件物品,第i(i从1开始)件物品的重量为w[i],价值为v[i]。在总重量不超过背包承载上限W的情况下,能够装入背包的最大价值是多少?

思路:
如果穷举的话,时间复杂度太大,故dp
定义状态dp:
dp[i][j]表示将前i件物品装进限重为j的背包可以获得的最大价值, 0<=i<=N, 0<=j<=W
初始化,当 i > 0 时dp[i][j]有两种情况:
不装入第i件物品,即dp[i−1][j];
装入第i件物品(前提是能装下),即dp[i−1][j−w[i]] + v[i]。

即其状态转移方程为:
dp[i][j] = max(dp[i−1][j], dp[i−1][j−w[i]]+v[i]) // j >=w[i]

特别的:为了防止上一层被覆盖,循环采用逆向枚举

// 01背包问题伪代码(空间优化版)
dp[0,...,W] = 0
for i = 1,...,Nfor j = W,...,w[i] // 必须逆向枚举!!!dp[j] = max(dp[j], dp[j−w[i]]+v[i])

采用这个方法的好处是避免了不必要的重复计算

//dfs
#include <stdlib.h>
#include <string.h>int Knapsack(int* v, int* w, int n, int target) {//Knapsack 函数接受三个参数:v、w 和 target,分别表示物品的体积、价值和背包的体积。
const int N = n + 1;
//申请大小为 (n+1) x (target+1) 的二维数组 cache 用于存储中间结果
int** cache = (int**)malloc(sizeof(int*) * N);
for (int i = 0; i < N; i++) {cache[i] = (int*)malloc(sizeof(int) * (target + 1));
memset(cache[i], -1, sizeof(int) * (target + 1));
}
int dfs(int i, int j) {//定义内部函数 dfs,用于递归求解问题
//参数 i 和 j,分别表示剩余的物品数量和背包的剩余体积
if (i == 0) return 0;//当没有物品可选时,价值为 0
if (cache[i][j] != -1) return cache[i][j];//cache[i][j] 已经有值,直接返回
//cache[i][j] 表示前 i 个物品放入体积为 j 的背包的最大价值
int res = dfs(i - 1, j);
if (j >= v[i - 1]) res = max(res, dfs(i - 1, j - v[i - 1]) + w[i - 1]);
//先递归求解不选第 i 个物品的情况,然后考虑选第 i 个物品
//第 i 个物品的体积超过当前背包剩余的体积,则必须放弃该物品
//放入背包并更新最大价值
cache[i][j] = res;
return res;
}
//调用 dfs(n, target) 求解问题,并释放动态分配的内存
int result = dfs(n, target);
for (int i = 0; i < N; i++) free(cache[i]);
free(cache);
return result;
}
//dfs->dp
#include <string.h>const int N = 1010;int Knapsack(int* v, int* w, int n, int target) {int dp[N][N]; memset(dp, 0, sizeof(dp));//dp 数组全部初始化为 0
//使用两个嵌套的循环来填充 dp 数组中的值
for (int i = 1; i <= n; i ++) {//枚举所有物品
for (int j = 0; j <= target; j ++) {//枚举所有体积
dp[i][j] = dp[i - 1][j];
//不选该物品,背包的价值就是前 i-1 个物品放入 j 体积的背包的价值
//选该物品,背包的价值就是前 i-1 个物品放入 j-v(i) 体积的背包的价值加上第 i 个物品的价值
//将价值较大的作为 dp(i, j) 的值
if (v[i - 1] <= j) dp[i][j] = max(dp[i][j], dp[i - 1][j - v[i - 1]] + w[i - 1]);
}
}
return dp[n][target];
}

dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - v[i]] + w[i])
1、对于每次循环的下一组i,只会用到i-1来更新当前值。于是可以在这次更新的时候,
将原来的值更新掉,反正以后也用不到,所以对于i的更新,只需用一个数组。也就
滚动数组,每次根据上一层的值更新当前层的值。(优化掉一维)
2、对于每次j的更新,只需用到之前i-1时的j或者j-v[i],不会用到后面的值,同时为
了防止当前用于更新的之前i-1层的d[j] 或 d[j - v[i]]之前被更新过,采用从后
往前的方式遍历j。(优化j的循环次数)

//dp
//滚动数组的思想来实现空间优化
int Knapsack(int* v, int* w, int n, int target) {const int N = 1010;
int dp[N]; memset(dp, 0, sizeof(dp));
for (int i = 1; i <= n; i ++) {for (int j = target; j >= v[i - 1]; j -- ) {dp[j] = max(dp[j], dp[j - v[i - 1]] + w[i - 1]);
}
}
return dp[target];
}

2. 完全背包

题目
完全背包与01背包不同就是每种物品可以有无限多个:一共有N种物品,每种物品有无限多个,第i(i从1开始)种物品的重量为w[i],价值为v[i]。在总重量不超过背包承载上限W的情况下,能够装入背包的最大价值是多少?

// 完全背包问题**Way1**伪代码(空间优化版)
dp[0,...,W] = 0
for i = 1,...,Nfor j = w[i],...,W // 必须正向枚举!!!dp[j] = max(dp[j], dp[j−w[i]]+v[i])

way2
取0件、1件、2件…直到超过限重(k > j/w[i])
思路与01问题相同,就是MAX改了

k为装入第i种物品的件数,
k <= j/w[i] dp[i][j] = max{(dp[i-1][j − kw[i]] +kv[i]) for every k}

// 完全背包问题**Way2**伪代码(空间优化版)
dp[0,...,W] = 0
for i = 1,...,Nfor j = W,...,w[i] // 必须逆向枚举!!!for k = [0, 1,..., j/w[i]]dp[j] = max(dp[j], dp[j−k*w[i]]+k*v[i])

二进制的思想

把第 i 种物品拆成
重量为

价值为

的若干件物品,其中 k 取遍满足
的非负整数。

这是因为不管最优策略选几件第 i 种物品,总可以表示成若干个刚才这些物品的和
这样就将转换后的物品数目降成了对数级别。

#include <stdio.h>
#include <string.h>#define N 1010int Knapsack(int *v, int *w, int target, int n) { // target表示背包体积
int dp[N][N];
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= n; i ++ ) {for (int j = 0; j <= target; j ++ ) {for (int k = 0; k * v[i - 1] <= j; k ++ ) {dp[i][j] = (dp[i][j] > dp[i - 1][j - k * v[i - 1]] + k * w[i - 1]) ? dp[i][j] : dp[i - 1][j - k * v[i - 1]] + k * w[i - 1];
}
}
}
return dp[n][target];
}int main() {int v[] = {2, 3, 4};
int w[] = {3, 4, 5};
int target = 5;
int n = sizeof(v) / sizeof(int);
printf("The maximum value of the knapsack is %d.\n", Knapsack(v, w, target, n));
return 0;
}
//优化
#include <stdio.h>
#include <string.h>#define N 1010int Knapsack(int *v, int *w, int target, int n) { // target表示背包体积
int dp[N][N];
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= n; i ++ ) {for (int j = 0; j <= target; j ++ ) {dp[i][j] = dp[i - 1][j];
if (j >= v[i - 1]) dp[i][j] = (dp[i - 1][j] > dp[i][j - v[i - 1]] + w[i - 1]) ? dp[i - 1][j] : dp[i][j - v[i - 1]] + w[i - 1];
}
}
return dp[n][target];
}int main() {int v[] = {2, 3, 4};
int w[] = {3, 4, 5};
int target = 5;
int n = sizeof(v) / sizeof(int);
printf("The maximum value of the knapsack is %d.\n", Knapsack(v, w, target, n));
return 0;
}

动态规划之背包问题系列(一)相关推荐

  1. 动态规划之背包问题——01背包

    算法相关数据结构总结: 序号 数据结构 文章 1 动态规划 动态规划之背包问题--01背包 动态规划之背包问题--完全背包 动态规划之打家劫舍系列问题 动态规划之股票买卖系列问题 动态规划之子序列问题 ...

  2. 动态规划之股票买卖系列问题

    文章目录 一.股票买卖系列问题 二.leetcode例题讲解股票买卖系列问题 121. 买卖股票的最佳时机 122. 买卖股票的最佳时机 II 123. 买卖股票的最佳时机 III 188. 买卖股票 ...

  3. Suzy心情很差因为被charge了late fee Day42 | 动态规划之背包问题,416. 分割等和子集

    背包问题 01背包 Given n种物品,每种物品只有1个 每个物品有自己的重量.价值. 背包最大能装载的重量 动规五部曲--用二维数组 定义dp数组的含义:dp[ i ][ j ]表示[0,i]物品 ...

  4. 【算法与数据结构】—— 动态规划之背包问题

    动态规划之背包问题 前面介绍了一些最常见的动态规划题型和对应解法,而实际上,动态规划最经典的题型非背包问题莫属,并且大多数人最初都是从背包问题入坑进而打开动态规划这一大门. 背包问题分为多种,其中最常 ...

  5. 动态规划之背包问题的一些基础简单入门题

    前言 参考视频教程洛谷试练场 普及组 动态规划的背包问题 主要有01背包问题.完全背包问题.分组背包问题. 01背包问题一般从右往左推: 完全背包问题一般从左往右推: 分组背包一般用01的方法但需要记 ...

  6. c语言背包问题装字母,C语言动态规划之背包问题详解

    01背包问题 给定n种物品,和一个容量为C的背包,物品i的重量是w[i],其价值为v[i].问如何选择装入背包的物品,使得装入背包中的总价值最大?(面对每个武平,只能有选择拿取或者不拿两种选择,不能选 ...

  7. 动态规划之背包问题(JAVA)

    背包问题之前的C语言版本已经将思路解析的差不多,虽然还有些许错误需要改正,但大体思路是正确的,需要的读者请参阅动态规划之背包问题(C语言) 背包问题本身就是典型的动态规划问题,所以这里只给出动态规划的 ...

  8. 动态规划解决背包问题

    动态规划解决背包问题 问题描述: (1)解法一: 解决思路:动态规划 状态索引范围:从1开始 价值数组,大小数组索引范围:从0开始 状态:F(i,j):前i个物品放入大小为j的背包中所获得的最大价值. ...

  9. 动态规划之背包问题总结

    动态规划之背包问题总结 递推公式 遍历顺序 01背包遍历顺序 完全背包遍历顺序 参考链接:代码随想录   背包问题是动态规划中的重要的一部分,背包问题分为多种,只需要掌握常见的01背包和完全背包就行. ...

最新文章

  1. Hsiaoyang: Google与站点地图Sitemap
  2. echarts如何获取后端的值_Echarts 获取后台数据 使用后台数据展示 柱形图
  3. trident State应用指南
  4. Android studio 配置使用maven
  5. Windows漏洞:MS08-067远程代码执行漏洞复现及深度防御
  6. skia 源码分析_【脚下有根】之Skia库的matrix代码解读
  7. vivado中bit文件怎么没有生成_Xilinx FPGA bit 文件加密
  8. 计算机网络—路由器(Cisco Packet Tracer)常用的命令总结
  9. 防毒墙APT防护抗DDOS攻击
  10. 以太网速率怎么手动设置_交换机如何配置速率和双工模式
  11. 指纹传感器安全性究竟有多少?
  12. 55岁的大妈被儿媳嫌弃,二次创业,靠洗地毯一年就开了一家公司!
  13. java 倍数_Java硬币翻转倍数递增试算实例
  14. java经典随机加减法游戏
  15. 如何解除硬盘的磁盘写保护?
  16. 高等数学——砍瓜切菜算积分的分部积分法
  17. 线下店迎来“文艺复兴”,三只松鼠哪里寻“独门妙方”?
  18. 计算机毕业设计(42)java小程序毕设作品之小说电子书阅读小程序系统
  19. 黑客都用Python做什么?我们能学会这些技术吗?
  20. Codeforces Beta Round #4 (Div. 2 Only)

热门文章

  1. 朋友圈转发截图生成工具源码
  2. 遍历List集合中的Map集合:
  3. oprn/read/write/close系统函数
  4. 计算机二级python用什么版本好_如何准备全国计算机二级Python?
  5. PHPMailer实现简单邮箱验证码
  6. 青云QingCloud首推Anybox企业云盘 新品牌新产品带给企业用户新体验
  7. Linux下C语言编程实现sleep函数
  8. 42表盘直径是从哪测量_表盘直径多大才合适?如何测量手表表盘的直径?
  9. 2022年全球十大IC设计公司最新排名出炉
  10. 2020年了,大数据之父舍恩伯格又有什么新预测?