背包
01 背包
\(n\) 件物品,每件物品有权值和重量,给出背包体积 \(V\),从这些物品中挑选若干件(只能选一次)放入背包,使得背包内物品的总重量不超过 \(V\),问能可以得到的最大权值。
设 \(dp[i][j]\) 选取前 \(i\) 件物品重量为 \(j\) 能取得的最大的权值,可以得到转移方程 \(dp[i][j]=max(dp[i-1][j-w[i]]+val[i],dp[i-1][j]);\)
因为 i 只与 i-1 有关所以考虑滚动数组:
交替滚动
用 now 表示现在的数组,用 old 表示上一个数组,交替使用。
#include<bits/stdc++.h> using namespace std; const int N=10010; int t,m; int w[202],val[202]; int dp[3][10010]; int main(){ ios::sync_with_stdio(false); cin>>t>>m; for(int i=1;i<=m;i++){ cin>>w[i]>>val[i]; } int now=0,old=1; for(int i=1;i<=m;i++){ swap(old,now); for(int j=0;j<=t;j++){ if(j>=w[i]){ dp[now][j]=max(dp[old][j],dp[old][j-w[i]]+val[i]); } else{ dp[now][j]=dp[old][j]; } } } cout<<dp[now][t]; return 0; }
自我滚动
因为体积只会向小减少,所以从后向前遍历容量以确保每个数物品只选一次,上一次的数组储存的数(i-1) 结果全部就在这个数组里,这样空间压缩到一维。
#include<bits/stdc++.h> using namespace std; const int N=10010; int t,m; int w[202],val[202]; int dp[10010]; int main(){ ios::sync_with_stdio(false); cin>>t>>m; for(int i=1;i<=m;i++){ cin>>w[i]>>val[i]; } for(int i=1;i<=m;i++){ for(int j=t;j>=w[i];j--){ dp[j]=max(dp[j],dp[j-w[i]]+val[i]); } } cout<<dp[t]; return 0; }
完全背包
\(n\) 件物品,每件物品有权值和重量,给出背包体积 \(V\),从这些物品中挑选若干件(可以多次同选一个)放入背包,使得背包内物品的总重量不超过 \(V\),问能可以得到的最大权值。
我们对 01 背包的代码进行更改为从小到大枚举容量,因为在计算某个容量时,可以确保之前计算的状态已经包含了该物品之前选择的次数,这样就能够正确地累积多个相同物品的价值。
#include<bits/stdc++.h> using namespace std; const int N=10010; int t,m; int w[202],val[202]; int dp[10010]; int main(){ ios::sync_with_stdio(false); cin>>t>>m; for(int i=1;i<=m;i++){ cin>>w[i]>>val[i]; } for(int i=1;i<=m;i++){ for(int j=w[i];j<=t;j++){ dp[j]=max(dp[j],dp[j-w[i]]+val[i]); } } cout<<dp[t]; return 0; }
多重背包
\(n\) 件物品,每件物品有权值和重量,给出背包体积 \(V\),从这些物品中挑选若干件(给出共有几个)放入背包,使得背包内物品的总重量不超过 \(V\),问能可以得到的最大权值。
考虑将每个物品进行二进制拆分,拆完后就转化为 01 背包问题。(因为是二进制,所以可以保证可以取到任意组合)
#include<bits/stdc++.h> using namespace std; const int N=1e6+10; int t,m; int w[N],val[N]; long long dp[N]; int main(){ ios::sync_with_stdio(false); cin>>m>>t; int a,b,c; int cnt=0; for(int i=1;i<=m;i++){ cin>>a>>b>>c; for(int j=1;j<=c;j<<=1){//二进制 w[++cnt]=j*a;//第cnt个背包的重量 val[cnt]=j*b;//计算权值 c-=j;//计算剩余 } if(c){//剩余单独储存 w[++cnt]=c*a; val[cnt]=c*b; } } for(int i=1;i<=cnt;++i){//01背包选择 for(int j=t;j>=w[i];--j){ dp[j]=max(dp[j],dp[j-w[i]]+val[i]); } } cout<<dp[t]; return 0; }
分组背包
01背包规则,他的物品大致可分为 \(k\) 组,每组中的物品相互冲突,现在,问最大的价值是多少。
只是加入了组的限制,只需按组储存,然后 01 背包。
#include<bits/stdc++.h> using namespace std; int n,m; int w[10001]; int val[10001]; int b[100001]; int g[205][205]; int dp[100001]; int main(){ ios::sync_with_stdio(false); cin>>m>>n; int t=0; for(int i=1;i<=n;i++){ int x;//组 cin>>w[i]>>val[i]>>x; t=max(t,x);//最大组 b[x]++;//小组物品增加 g[x][b[x]]=i;//第 x 组第 b[x] 个物品的编号 } for(int i=1;i<=t;i++){//枚举组数 for(int j=m;j>=0;j--){//枚举容量 for(int k=1;k<=b[i];k++){//枚举第i组的物品 if(j>=w[g[i][k]]){ dp[j]=max(dp[j],dp[j-w[g[i][k]]]+val[g[i][k]]); } } } } cout<<dp[m]; return 0; }
混合背包
背包中的物品有的可以选一次(0/1 背包),有的可以选多次(完全背包),有的数量有限(多重背包)。
无限个并不是真的无限,只需转化为最大容积除物品中重量的个数就行,然后问题就转化为多重背包了。
#include<bits/stdc++.h> using namespace std; const int N=1e6+10; int t,m; int w[N],val[N]; long long dp[N]; int main(){ ios::sync_with_stdio(false); cin>>t>>m; int a,b,c; int cnt=0; for(int i=1;i<=m;i++){ cin>>a>>b>>c;//重,价,数 if(c==0){//无限个(最大容积除重量个) c=t/a; } for(int j=1;j<=c;j<<=1){//剩下多重背包 w[++cnt]=j*a; val[cnt]=j*b; c-=j; } if(c){ w[++cnt]=c*a; val[cnt]=c*b; } } for(int i=1;i<=cnt;++i){ for(int j=t;j>=w[i];--j){//01背包 dp[j]=max(dp[j],dp[j-w[i]]+val[i]); } } cout<<dp[t]; return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)