01背包问题详解
背包问题详解
01背包
1.1引入
01背包问题实例:
假如你有一个背包,可以装10千克物品,你现在可以选择携带:
- 三明治:0.5千克,价值10元
- 水果:1千克,价值15元
- 帐篷:4千克,价值30元
- 饮料:3千克,价值5元
- 地毯:3.5千克,价值20元
如果每样东西都最多只能带一件,如何选择才能让物品的总价值最大?
1.2为什么不能使用暴力
如果有四样物品,那么有\(2^4=16\)种可能性
如果有十样物品,那么有1024种可能性
但如果有100件物品,那么就会有\(1267650600228229401496703205376\)种可能性
你的代码就会成功的\(TLE\)
1.3动态规划的解决思路
动态规划就是用表格记录下子问题的最优解
举例:
请点击下方网址查看数据👇
https://img2024.cnblogs.com/blog/3799445/202607/3799445-20260720155007356-1339556381.png
(背包容量为10)
| 背包容量 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 物品0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 物品1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
| 物品2 | 0 | 0 | 1 | 2 | 3 | 4 | 4 | 4 | 4 | 4 | 4 |
| 物品3 | 0 | 0 | 1 | 3 | 5 | 5 | 6 | 8 | 8 | 9 | 9 |
| 物品4 | 0 | 0 | 1 | 3 | 5 | 5 | 6 | 9 | 9 | 10 | 12 |
| 物品5 | 0 | 0 | 1 | 3 | 5 | 5 | 8 | 9 | 9 | 11 | 13 |
最终的答案就是13。
1.4如何构建表格?
使用公式dp[i][w] = max(dp[i-1][w], dp[i-1][w-重量[i]] + 价值[i])将表格填完整,所有格子默认为0
1.5为什么这样做?
1. 问题可以拆碎
原问题是"n个物品装容量小于上线的物品",我们把它拆成"前1个装各种容量"、"前2个装各种容量"……每个小问题独立求解。
2. 小问题的答案能复用
算第i行时,直接抄上一行的结果就行。因为"前i个"的最优解,要么包含第i个,要么不包含——不包含时就和"前i-1个"完全一样。
3. 选第i个时,剩下的仍是已解决的子问题
如果决定要第i个,那就先腾出它的重量,剩下的容量去装前i-1个——这个子问题上一行已经算过了,直接查表取数加价值即可。
4. 每个格子只存最优值
表格不记录"选了哪些物品",只记录"最大价值是多少"。因为后续推导只需要这个数字,不需要具体组合。
1.6.1例题
P1048 [NOIP 2005 普及组] 采药
题目描述
辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”
如果你是辰辰,你能完成这个任务吗?
输入格式
第一行有 \(2\) 个整数 \(T\)(\(1 \le T \le 1000\))和 \(M\)(\(1 \le M \le 100\)),用一个空格隔开,\(T\) 代表总共能够用来采药的时间,\(M\) 代表山洞里的草药的数目。
接下来的 \(M\) 行每行包括两个在 \(1\) 到 \(100\) 之间(包括 \(1\) 和 \(100\))的整数,分别表示采摘某株草药的时间和这株草药的价值。
输出格式
输出在规定的时间内可以采到的草药的最大总价值。
输入输出样例 #1
输入 #1
70 3
71 100
69 1
1 2
输出 #1
3
说明/提示
【数据范围】
- 对于 \(30\%\) 的数据,\(M \le 10\);
- 对于全部的数据,\(M \le 100\)。
【题目来源】
NOIP 2005 普及组第三题
1.6.2解法
本题每种草药只有选和不选两种选择,所以是01背包
1.6.3样例分析
70 3
71 100
69 1
1 2
\(总时间 70,共 3 株草药\)
\(草药1:耗时71,价值100 → 采不了(71 > 70)\)
\(草药2:耗时69,价值1\)
\(草药3:耗时1,价值2\)
最优方案:\(采草药2 + 草药3 = 耗时70\),价值 \(1+2 = 3\)
1.6.4代码详解
点击此处查看代码
#include<bits/stdc++.h>//万能头文件
using namespace std;
int dp[1005][1005];//自动初始化为0
int main() {
int m,n;cin>>m>>n;//输入,注意m和n的顺序
int w,c;//w为重量,c为价值
for(int i=1;i<=n;i++) {//枚举行
cin>>w>>c;
for(int j=0;j<=m;j++) {//枚举列
dp[i][j]=dp[i-1][j];//默认为上一次此重量的价值
if(j>=w) {//如果重量足够携带此物品
dp[i][j]=max(dp[i][j],dp[i-1][j-w]+c);//状态转移方程,取最大值
}
}
}
cout<<dp[n][m];//输出答案
return 0;
}

浙公网安备 33010602011771号