01背包问题详解
引言
背包问题是动态规划(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
思路:
如果采用暴力枚举每一件物品放或者不放进背包,有两种选择,所以时间复杂度为,非常大。
接下来考虑动态规划求解。
题解一:先尝试二维解法。
我们可以定义一个二维数组dp存储最大价值,其中dp[i][j]
表示前i 件物品体积不超过j (即此时背包容量
为j)的情况下能达到的最大价值。
在我们遍历到第i 件物品时,在当前背包总容量为j 的情况下,
-
如果我们不将物品i 放入背包,那么
dp[i][j]= dp[i-1][j]
,即前i 个物品的最大价值等于只取前i-1 个物品时的最大价值;
-
如果我们将物品i 放入背包,假设第i 件物品体积为wi,价值为vi,那么我们得到
dp[i][j] = dp[i-1][j-w[i]] + v[i]
。我们只需在遍历过程中对这两种情况取最大值即可,总时间复杂度和空间复杂度都为。
综合上面提到的2种选择策略,我们可以得到状态转移方程:
dp[i][j] = max{dp[i-1][j],dp[i-1][j-w[i]] + v[i]}
确定初始化边界,dp[0][0] = 0
.
注意理解误区:
dp[i][j]
里的i
不是表示选择了前i个物品,而是表示对前i个物品做出两中策略的选择;
里面的j
不是表示当前物品的总体积等于j,而是表示前i 件物品体积不超过j 。
代码:(二维朴素做法)
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010;
int dp[N][N]; // dp[0][0] = 0
int v[N],w[N];
int n,m;
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++){
dp[i][j] = dp[i-1][j];
if (j - v[i] >= 0){
dp[i][j] = max(dp[i-1][j],dp[i-1][j-v[i]]+w[i]);
}
}
cout << dp[n][m];
return 0;
}
题解二:再尝试一维优化。
也就是对二维做法等价变形得到一维做法。
我们可以进一步对0-1 背包进行空间优化,将空间复杂度降低为。时间复杂度已经不能再优化了。
从二维变成一维,相当于把二维中第一个维度变成循环滚动只有1行的数组dp[N]
。
如果我们仍然从左往右计算dp[j]
,那么可能存在污染,因为后面的数据根据前面递推而来,在滚动的时候可能要用到dp[i-1]
(即上一次循环的数据时,实际上这个位置的数据已经在这次循环时被更新过了,用到的是dp[i]
的数据,那么就出错了。
只有通过逆序枚举v,即从右往左滚动数组,这次计算dp[i]
时依然根据上次循环递推而来,而且dp[i-v[i]]
并没有被污染,才能得到正确结果。
我们注意到在处理数据时,我们是一个物品一个物品,一个一个体积的枚举。
因此我们可以不必开两个数组记录体积和价值,而是边输入边处理。这样可以进一步压缩空间。
代码:
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010;
int dp[N];
int n,m;
int v,w;
int main(){
cin >> n >> m;
for (int i = 1;i <= n;i++){
cin >> v >> w; // 边输入边处理
for (int j = m;j >= v;j--){
dp[j] = max(dp[j],dp[j-v]+w);
}
}
cout << dp[m];
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异