动态规划-背包问题
简单背包(01背包)
有n件物品,已知每件物品的价值v[i]和重量w[i],背包容量为c,要求从这n件物品中任取若干件装入背包内,使背包的物品价值最大。
未优化写法
设dp[i][j]表示有i件物品,在背包容量为j的情况下的最大价值度,则最终的答案为dp[n][c]
分析dp数组的状态转移方程:
对于第i件物品,背包容量为j
- 如果w[i]>j,则放不下,那么
- 如果w[i]<=j, 放得下, 那么可以选,也可以不选,即
#include <bits/stdc++.h> using namespace std; int n,c,w[20010],v[110],dp[110][20010]; int main() { scanf("%d %d",&c,&n); for(int i=1;i<=n;i++) { scanf("%d %d",&w[i],&v[i]); } //动态规划 for(int i=1;i<=n;i++) { for(int j=1;j<=c;j++) { if(w[i]>j) dp[i][j]=dp[i-1][j]; else dp[i][j]=max(dp[i-1][j],v[i]+dp[i-1][j-w[i]]); } } //输出答案 printf("%d",dp[n][c]); return 0; }
优化写法-滚动数组
dp二维数组当中有很多空间是浪费的,因为第i行dp值的计算只和第i-1行有关,所以可以只用一个一维数组,实现滚动数组的方式来维护dp数组
方法:用一维dp数组,从后往前更新,这样dp[j]相当于原先的dp[i-1][j],而dp[j-w[i]]相当于原先的dp[i-1][j-w[i]]
注意,dp数组要从后往前更新,否则会覆盖原先数组的值,导致dp[i-1][j-w[i]]取到错误的值
此时状态转移方程变为:
这一步调整之后,时间复杂度和空间复杂度都会有很大的提升
#include <bits/stdc++.h> using namespace std; const int maxn = 2e5+10; int n,wi,vi,c; int dp[maxn]; int main() { scanf("%d %d",&c,&n); for(int i=1;i<=n;i++) { scanf("%d %d",&wi,&vi); //wi之前的相当于放不下的情况 可以不用算 直接继承自上一次的值 for(int j=c;j>=wi;j--) { //进来之后相当于之前的放得下的情况 需要在选和不选当中取出最大值 dp[j]=max(dp[j],vi+dp[j-wi]); } } //输出答案 printf("%d",dp[c]); return 0; }
完全背包
例:https://oj.czos.cn/p/1780
和01背包不同的是,每件物品有无限多个可以选择。
设dp[i][j]表示有i件物品,在背包容量为j的情况下的最大价值,那么很容易联想到完全背包的状态转移方程就是
下面对这个方程进行进一步的推导 尝试去掉k:
经过推广可以得到等价的方程:
其中的k先拿一个出来,得到
将①中k的范围扩大,可以得到③
将③中的j替换为j-w[i]之后得到
将④带入②,可以得出完全背包问题的最终方程:
与01背包的区别仅仅只是与的区别
#include <bits/stdc++.h> using namespace std; const int maxn = 1e5+10; int c,n,vi,wi; int dp[maxn]; int main() { //dp[i][j]=max(dp[i-1][j],dp[i][j-w[i]]+v[i]) scanf("%d %d",&c,&n); for(int i=1;i<=n;i++) { scanf("%d %d",&wi,&vi); //wi之前的不用计算 相当于之前的放不下的情况 for(int j=wi;j<=c;j++) { //wi开始 相当于之前的放得下的情况 正序递推dp数组 dp[j]=max(dp[j],dp[j-wi]+vi); } } printf("%d",dp[c]); return 0; }
多重背包 https://oj.czos.cn/p/1888
和01背包不同的是,每件物品有Si件
#include <bits/stdc++.h> using namespace std; const int maxn = 110; int n,c; int w[maxn],v[maxn],s[maxn],dp[maxn]; int main() { scanf("%d %d",&n,&c); for(int i=1;i<=n;i++) { scanf("%d %d %d",&w[i],&v[i],&s[i]); } for(int i=1;i<=n;i++) { for(int j=1;j<=s[i];j++) { for(int k=c;k>=w[i];k--) { dp[k]=max(dp[k],dp[k-w[i]]+v[i]); } } } printf("%d\n",dp[c]); return 0; }
多重背包的二进制优化 https://oj.czos.cn/p/1889
二进制优化是一种压缩的思想
(1)有n个不同的物品,要讨论种选择的可能(每个物品选或者不选):
(2)相同的物品有n件,虽然要讨论种选择的可能,但由于n个物品是一样的,那么就减少了讨论数量,比如:有4个物品,如果是不同物品的选2个,选12、23是不同的选择,但如果是相同的物品,选哪两个就都是一样的了。因此,n个物品,要讨论的可能就分别是:选0个、选1个、选2个、选3个…选n个。
(3)要将0-n个不同的选择表达出来,比较简单的方法是将n二进制化。比如:整数7,只需要用124三个数任意组合,就能组合出0-7这8种可能。再比如:整数10,只需要用1243(注意最后一个数),就能组合出0-10这11种可能,这样n这个值就被二进制化了。因此如果要讨论10个一样的物品,就转化为讨论4个不同的物品了;而n个一样的物品,就转化为个不同的物品进行讨论。
#include <bits/stdc++.h> using namespace std; const int maxn = 11100; int n,c,k,wi,vi,si; int w[maxn],v[maxn],s[maxn],dp[maxn]; int main() { scanf("%d %d",&n,&c); //读入数据 同时进行二进制压缩 for(int i=1;i<=n;i++) { scanf("%d %d %d",&wi,&vi,&si); int t = 1; while(t<=si) { k++; w[k] = t*wi; v[k] = t*vi; si -= t; t *= 2; //注意这里要放在后面 } if(si>0) { k++; w[k] = si*wi; v[k] = si*vi; } } //01背包 for(int i=1;i<=k;i++) { for(int j=c;j>=w[i];j--) { dp[j]=max(dp[j],dp[j-w[i]]+v[i]); } } printf("%d\n",dp[c]); return 0; }
混合背包 https://oj.czos.cn/p/1905
二进制压缩后就只剩下01背包和完全背包了
#include <bits/stdc++.h> using namespace std; const int maxn = 10500; int vi,wi,si,n,c,k; int v[maxn],w[maxn],s[maxn],dp[maxn]; int main() { scanf("%d %d",&n,&c); for(int i=1;i<=n;i++) { scanf("%d %d %d",&wi,&vi,&si); //多重背包转换为01背包 if(si>0){ int t = 1;//权重 while(t<=si) { k++; v[k] = t*vi; w[k] = t*wi; s[k] = -1; si -= t; t *= 2; } if(si>0) { k++; v[k] = si*vi; w[k] = si*wi; s[k] = -1; } }else{ //01背包和完全背包都是正常存储即可 k++; w[k] = wi; v[k] = vi; s[k] = si; } } //剩下01背包和完全背包的dp for(int i=1;i<=k;i++) { //01背包 if(s[i]==-1) { for(int j=c;j>=w[i];j--) { dp[j]=max(dp[j],dp[j-w[i]]+v[i]); } }else{ //完全背包 for(int j=w[i];j<=c;j++) { dp[j]=max(dp[j],dp[j-w[i]]+v[i]); } } } //输出答案 printf("%d",dp[c]); return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探