先来看模版题目:
1.小明的背包1

这个便是最经典的求最大价值的01背包问题:
暴力求解的话便是dfs每个选或不选,决策树的问题,这个时间复杂度太高了暂且不说。
设dp[i][j]为前i个物品装进容量为j的背包所能产生的最大价值;
接下来考虑状态转移方程:假设某种放的方法成功达到了最大值,对于这个方法:对于第i个物品来说,有两种可能的情况,第一种是这种方法包括他,第二种是不包括,取两种方法的最大值,
如果放入第i个物品的话,那么获得第i个物品的价值,接下来的背包就是从前i-1个元素中选择若干物品放入容量为v-w[i]的背包中,如果不放入,那么便是从i-1个物品中放入容量为v的背包中,
即
dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+c[i]);
边界处理问题:
这里边界的处理是由转移方程得到的,其实第二个for循环可以不用,赋值10086也是能通过的
dp[0][0]=0;
for(int i=1;i<=v;i++){
dp[0][i]=0;
}
for(int i=1;i<=n;i++){
dp[i][0]=0;
}
接下来来看状态转移的代码实现
for(int i=1;i<=n;i++){
for(int j=0;j<=v;j++){
if(j<w[i])
dp[i][j]=dp[i-1][j];
else
dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+c[i]);
}
}
这里要考虑j<w[i]时是一定不能选的
这道题目还可以进行空间优化,因为这里状态转移方程只取决于上一维度的dp数组,这里j要从大到小遍历,否则每次更新将会覆盖上一层的元素,而这些元素可能在j更大时需要用到
for(int i=0;i<=v;i++){
dp[i]=0;
}
for(int i=1;i<=n;i++){
for(int j=v;j>=0;j–){
if(j>=w[i])
dp[j]=max(dp[j],dp[j-w[i]]+c[i]);
}
}
01背包的进阶问题:完全背包:
这里物品不再是以1个了而是可以无限去选择了 1.小明的背包2 – 蓝桥云课

对于完全背包来说,dp数组的状态定义是不变的,但是状态转移方程要变了,这里如果选择拿的话就要枚举所有能拿的情况,一个?或者两个?或者更多,
for(int i=1;i<=n;i++){
for(int j=0;j<=v;j++){
dp[i][j]=0;
for(int k=0;k<=j/w[i];k++){
dp[i][j]=max(dp[i][j],k*c[i]+dp[i-1][j-k*w[i]]);
}
}
完全背包的空间优化,时间优化:
先看时间优化:
对于第i个商品拿或者不拿,也可以看成不拿以及至少拿一个,既然是至少拿一个的话那可以先放一个进背包,就变成了dp[i][j-w[i]]+c[i]
for(int i=1;i<=n;i++){
for(int j=1;j<=v;j++){
if(j<w[i])
dp[i][j]=dp[i-1][j];
else
dp[i][j]=max(dp[i-1][j],dp[i][j-w[i]]+c[i]);
}
}
同时空间也可以优化(注意这里就要从小到大遍历了)dp[j-w[i]]+c[i]这个是同一行的结果而非要上一行的结果。
for(int i=0;i<=v;i++){
dp[i]=0;
}
for(int i=1;i<=n;i++){
for(int j=0;j<=v;j++){
if(j>=w[i])
dp[j]=max(dp[j],dp[j-w[i]]+c[i]);
}
}
背包dp的变种问题:
1,不再求最大价值,转而去求有多少种方案数的问题
U663298 疯狂的背包问题(3) – 01背包问题(计数组合问题) – 洛谷
这里dp数组的状态定义就要由最大价值改为方案数了
#include<bits/stdc++.h>
using namespace std;
#define int long long
int ans=0;
int dp[1001][1001];
//dp[i][j]=dp[i-1][j]+dp[i-1][j-w[i]];
int w[1001];
signed main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>w[i];
}
dp[0][0]=1;
for(int i=1;i<=m;i++){
dp[0][i]=0;
}
for(int i=1;i<=n;i++){
for(int j=0;j<=m;j++){
if(j>=w[i])
dp[i][j]=(dp[i-1][j]+dp[i-1][j-w[i]])%1000000007;
else
dp[i][j]=dp[i-1][j]%1000000007;
}
}
cout<<dp[n][m]%1000000007;
return 0;
}
2,无限背包变种:518. 零钱兑换 II – 力扣(LeetCode)
作者最近几天用脑过度了,待更新。。。
到这里,背包 dp 的核心内容就梳理得差不多了。我们从最经典的 01 背包出发,理解了状态定义、状态转移方程和边界处理,也掌握了空间优化的思路;接着扩展到完全背包,看到了枚举拿取数量的朴素写法,以及时间、空间上的双重优化;最后还介绍了背包 dp 的常见变种,比如求方案数的问题。背包 dp 是动态规划里非常基础也非常重要的一类模型,很多看似复杂的问题,最终都能化归到背包的框架下求解。希望这篇总结能帮你把背包 dp 的脉络理清楚,也欢迎在评论区交流你的想法,后续我会继续补充更多变种和实战题目。

![P11246 [GESP202409 六级] 小杨和整数拆分-171主机测评](https://www.171host.com/wp-content/uploads/2026/11/20261108100631-6af049a75855c-220x150.png)
![打卡信奥刷题(3678)用C++实现信奥题 P12274 [蓝桥杯 2024 国 Python B] 设计-171主机测评](https://www.171host.com/wp-content/uploads/2026/11/20261108053830-6af00ad6bdf8d-220x150.png)

