[笔记]分组背包
于 2024/11/25 修改分类 题解 \(\Longrightarrow\) 笔记。
分组背包模板题。
总共\(s\)组,每组最多取一个物品,实际上就是一个物品总数为\(s\)的背包。
for(int i=1;i<=s;i++){//枚举组 for(int j=1;j<=n[i];j++){//枚举每组的元素 for(int k=1;k<=m;k++){//枚举背包大小 f[i][k]=max(f[i][k],f[i-1][k]); if(k>=w[i][j]) f[i][k]=max(f[i][k],f[i-1][k-w[i][j]]+v[i][j]); } } }
仔细观察,可以发现代码和01背包十分相似,只不过外面套了一个枚举组的循环。
但是还有一点和01背包有区别,就是第\(4\)行,还需要和自己比较一次,这是因为我们之前枚举同组的其他元素时,可能已经给\(f[i][k]\)赋值了,所以不能忽略,需要参与比较。
接下来我们考虑如何优化空间。
for(int i=1;i<=s;i++){ for(int k=m;k>=1;k--){ for(int j=1;j<=n[i];j++){ if(k>=w[i][j]) f[k]=max(f[k],f[k-w[i][j]]+v[i][j]); } } }
与上面的代码相比,我们修改了两个部分:
- \(k\)改为从大到小。
这是为了让\(f[k-w[i][j]]\)求到的是上一层的、未修改过的值,与01背包的空间优化相似。 - 将枚举\(j,k\)的循环反转位置。
我们发现,如果把\(k\)放在最内层的话,\(j\)每增加\(1\),\(f\)的内容就要翻新一遍,这样我们求的\(f[k-w[i][j]]\)就已经修改过了,并不是上一层的原值了,所以我们把\(j\)放在最里面。
时间复杂度\(O(\sum^{s}_{i=1}n[i]*m)\)。
空间复杂度\(O(m)\)。
下面放上完整代码:
优化前Code
#include<bits/stdc++.h> using namespace std; int n[110],m,t,s; int w[110][1010],v[110][1010]; int f[110][1010]; int main(){ cin>>m>>t; for(int i=1;i<=t;i++){ int a,b,c; cin>>a>>b>>c; n[c]++; w[c][n[c]]=a; v[c][n[c]]=b; s=max(s,c); } for(int i=1;i<=s;i++){ for(int j=1;j<=n[i];j++){ for(int k=1;k<=m;k++){ f[i][k]=max(f[i][k],f[i-1][k]); if(k>=w[i][j]) f[i][k]=max(f[i][k],f[i-1][k-w[i][j]]+v[i][j]); } } } cout<<f[s][m]; return 0; }
优化后Code
#include<bits/stdc++.h> using namespace std; int n[110],m,t,s; int w[110][1010],v[110][1010]; int f[1010]; int main(){ cin>>m>>t; for(int i=1;i<=t;i++){ int a,b,c; cin>>a>>b>>c; n[c]++; w[c][n[c]]=a; v[c][n[c]]=b; s=max(s,c); } for(int i=1;i<=s;i++){ for(int k=m;k>=1;k--){ for(int j=1;j<=n[i];j++){ if(k>=w[i][j]) f[k]=max(f[k],f[k-w[i][j]]+v[i][j]); } } } cout<<f[m]; return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效