【C++算法模板】背包九讲(下):混合背包、二维费用背包、带依赖的背包、背包求方案数、背包求具体方案

2024-04-14 13:36

本文主要是介绍【C++算法模板】背包九讲(下):混合背包、二维费用背包、带依赖的背包、背包求方案数、背包求具体方案,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

    • 1)混合背包
    • 2)二维费用背包
    • 3)带依赖的背包
    • 4)背包求方案数
    • 5)背包求具体方案

1)混合背包

时间复杂度: O ( n 2 l o g 2 ) O(n^2log^2) O(n2log2),空间复杂度: O ( n ) O(n) O(n)

  • 关键点在于将多重背包二进制优化后变成 01 01 01背包和 01 01 01背包一起处理,完全背包单独处理,其实就是混合考了几种背包的处理方式
#include<bits/stdc++.h>
#define x first
#define y secondusing namespace std;typedef long long ll;
typedef pair<int,int> PII;// 解题思路: 三种物品三种放法,运用每组问题的解题思路即可const int N=1e3+5;int n,m;
int f[N];struct Thing {int kind; // 01?完全?多重?int v,w;
};
vector<Thing> things;int main() {cin>>n>>m;for(int i=0;i<n;i++) {int v,w,s;scanf("%d%d%d",&v,&w,&s);// 01背包问题if(s<0) things.push_back({-1,v,w});// 完全背包问题else if(s==0) things.push_back({0,v,w});// 多重背包问题else {for(int k=1;k<=s;k*=2) {s-=k;things.push_back({-1,v*k,w*k}); // 转换成01背包}if(s>0) things.push_back({-1,v*s,w*s});}}// 处理所有thingfor(auto item:things) {// 01背包/多重背包的处理→if(item.kind<0) {for(int j=m;j>=item.v;j--) {f[j]=max(f[j],f[j-item.v]+item.w);}}// 完全背包的处理→else {for(int j=item.v;j<=m;j++) {f[j]=max(f[j],f[j-item.v]+item.w);}}}cout<<f[m]<<endl;return 0;
}

2)二维费用背包

时间复杂度: O ( n 3 ) O(n^3) O(n3),空间复杂度: O ( n 2 ) O(n^2) O(n2)

  • 除了体积限制外加入了重量限制,处理方法和 01 01 01背包完全类似,只不过多了一重循环,和一维空间
#include<bits/stdc++.h>
#define x first
#define y secondusing namespace std;typedef long long ll;
typedef pair<int,int> PII;// 解题思路: const int N=1e3+5;
const int V=1e2+5; // 最大体积
const int M=1e2+5; // 最大载重
int n,v,m;
int f[V][M]; // 体积是i重量是j的最大价值int main() {cin>>n>>v>>m;for(int i=1;i<=n;i++) {// 边输入边处理int a,b,c;scanf("%d%d%d",&a,&b,&c);for(int j=v;j>=a;j--) {for(int k=m;k>=b;k--) {f[j][k]=max(f[j][k],f[j-a][k-b]+c);}}}cout<<f[v][m]<<endl;return 0;
}

3)带依赖的背包

时间复杂度: O ( n 3 ) O(n^3) O(n3),空间复杂度: O ( n ) O(n) O(n)

#include<bits/stdc++.h>
#define x first
#define y secondusing namespace std;typedef long long ll;
typedef pair<int,int> PII;// 解题思路: 选子物品前必须要选父物品const int N=1e2+5;
int n,m;
int h[N],e[N],ne[N],idx;
int v[N],w[N];
int f[N][N]; // f[i][j]:在选节点j的情况下总体积<=j,以i为根的子树的最大总收益是多少?// 每个子节点是一个物品组,每个组里面只能选一个,就变成了分组背包问题void add(int a,int b) {e[idx]=b;ne[idx]=h[a];h[a]=idx++;
}void dfs(int u) {// 循环物品组for(int i=h[u];i!=-1;i=ne[i]) {int son=e[i]; dfs(son);// 枚举背包容量,因为一定要选择根节点// 所以j-v[u],01背包从大到小枚举for(int j=m-v[u];j>=0;j--) {// 枚举决策,这个组里面选哪个// 枚举这个子节点用哪个体积for(int k=0;k<=j;k++) {f[u][j]=max(f[u][j],f[u][j-k]+f[son][k]);}}}// 如果体积大于等于当前物品体积,把之前空出来的位置把物品价值加进去for(int i=m;i>=v[u];i--) f[u][i]=f[u][i-v[u]]+w[u];// 如果体积小于当前物品体积,整棵子树一个点都不能选for(int i=0;i<v[u];i++) f[u][i]=0;
}int main() {memset(h,-1,sizeof h);cin>>n>>m;int root;for(int i=1;i<=n;i++) {int p;scanf("%d%d%d",&v[i],&w[i],&p); // p表示依赖关系if(p==-1) root=i; // -1表示根节点else add(p,i);}dfs(root);cout<<f[root][m]<<endl; // 根节点为root背包最大容量为m的最大价值return 0;
}

4)背包求方案数

时间复杂度: O ( n 2 ) O(n^2) O(n2),空间复杂度: O ( n ) O(n) O(n)

  • 01 01 01 背包的基础上要求求出能得到最大价值的方案数共有多少种
  • 若初始化 f [ 1 ] f[1] f[1] f [ m ] f[m] f[m],那么 f [ j ] f[j] f[j] 代表的是背包容量不超过 j j j 时所得最大价值,为了便于统计,我们想让 f [ j ] f[j] f[j] 表示背包容量恰为 j j j 时的最大价值,所以需要把 f [ 1 ] f[1] f[1] f [ m ] f[m] f[m] 初始化为 − I N F -INF INF
  • 因为最优解不一定在 f [ m ] f[m] f[m] 空间不一定用完,所以还需要枚举出最大价值,再把最大价值对应的方案数累加起来,才是最终结果
#include<bits/stdc++.h>
#define x first
#define y secondusing namespace std;typedef long long ll;
typedef pair<int,int> PII;// 解题思路: 求最大价值的选法有多少种
// 为了便于统计,我们要让物理意义变为背包容量恰为j时的最大价值
// 所以要初始化为负无穷const int N=1e3+5;
const int mod=1e9+7; // 答案很大
const int INF=1e6;int n,m;
int f[N],g[N];int main() {cin>>n>>m;g[0]=1; // 初始化,背包容量为0时方案数是1// 背包容量为[1,m]时最大价值初始化为-INFfor(int i=1;i<=m;i++) {f[i]=-INF;}for(int i=0;i<n;i++) {int v,w;cin>>v>>w;for(int j=m;j>=v;j--) {// 选与不选的最大值int t=max(f[j],f[j-v]+w);int s=0;// 看哪种方案更优,把其方案数拿过来// 因为有可能f[j]=f[j-v]+w,即从两个状态转移过来都可以// 所以写两个并列的if,可以都加if(t==f[j]) s+=g[j];if(t==f[j-v]+w) s+=g[j-v];if(s>=mod) s-=mod; // 手动取模f[j]=t;g[j]=s;}}int maxw=0;// 求最优解,最优解不一定是m,因为物理意义变了for(int i=0;i<=m;i++) maxw=max(maxw,f[i]);int res=0;// 求总的方案数for(int i=0;i<=m;i++) {if(maxw==f[i]) {res+=g[i];if(res>=mod) res-=mod;}}cout<<res<<endl;return 0;
}

5)背包求具体方案

时间复杂度: O ( n 2 ) O(n^2) O(n2),空间复杂度: O ( n 2 ) O(n^2) O(n2)

#include<bits/stdc++.h>
#define x first
#define y secondusing namespace std;typedef long long ll;
typedef pair<int,int> PII;// 解题思路: 要求输出字典序最小的一种选法(123<31)按位比
// 看f[n][m]是从哪个状态转移过来的,若为f[n-1][m](没选),若为f[n-1][m-v[i]]+w[i](选了)
// 贪心求,如果能选第一个物品,那么必须选第一个物品,这样字典序是最小的,前面的物品能选则选
// 从后往前推,求方案从前往后推const int N=1e3+5;
int n,m;
int v[N],w[N],f[N][N]; // 前i个物品中背包容量不超过j的方案int main() {cin>>n>>m;for(int i=1;i<=n;i++) {scanf("%d%d",&v[i],&w[i]);	}// 从后往前推,这样求出来的方案才是字典序最小的for(int i=n;i>=1;i--) {// 二维for(int j=0;j<=m;j++) {f[i][j]=f[i+1][j];if(j>=v[i]) {f[i][j]=max(f[i][j],f[i+1][j-v[i]]+w[i]);}}}int i=1,j=m; // 从后往前推,最大值是f[1][m]// 从前往后推最大值while(i<=n) {if(j>=v[i] && f[i+1][j-v[i]]+w[i]>=f[i+1][j]) {cout<<i<<' ';j-=v[i];i++;} else {i++;}}return 0;
}

这篇关于【C++算法模板】背包九讲(下):混合背包、二维费用背包、带依赖的背包、背包求方案数、背包求具体方案的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/903120

相关文章

Java中的雪花算法Snowflake解析与实践技巧

《Java中的雪花算法Snowflake解析与实践技巧》本文解析了雪花算法的原理、Java实现及生产实践,涵盖ID结构、位运算技巧、时钟回拨处理、WorkerId分配等关键点,并探讨了百度UidGen... 目录一、雪花算法核心原理1.1 算法起源1.2 ID结构详解1.3 核心特性二、Java实现解析2.

从入门到精通C++11 <chrono> 库特性

《从入门到精通C++11<chrono>库特性》chrono库是C++11中一个非常强大和实用的库,它为时间处理提供了丰富的功能和类型安全的接口,通过本文的介绍,我们了解了chrono库的基本概念... 目录一、引言1.1 为什么需要<chrono>库1.2<chrono>库的基本概念二、时间段(Durat

C++20管道运算符的实现示例

《C++20管道运算符的实现示例》本文简要介绍C++20管道运算符的使用与实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录标准库的管道运算符使用自己实现类似的管道运算符我们不打算介绍太多,因为它实际属于c++20最为重要的

Visual Studio 2022 编译C++20代码的图文步骤

《VisualStudio2022编译C++20代码的图文步骤》在VisualStudio中启用C++20import功能,需设置语言标准为ISOC++20,开启扫描源查找模块依赖及实验性标... 默认创建Visual Studio桌面控制台项目代码包含C++20的import方法。右键项目的属性:

SQLite3 在嵌入式C环境中存储音频/视频文件的最优方案

《SQLite3在嵌入式C环境中存储音频/视频文件的最优方案》本文探讨了SQLite3在嵌入式C环境中存储音视频文件的优化方案,推荐采用文件路径存储结合元数据管理,兼顾效率与资源限制,小文件可使用B... 目录SQLite3 在嵌入式C环境中存储音频/视频文件的专业方案一、存储策略选择1. 直接存储 vs

c++中的set容器介绍及操作大全

《c++中的set容器介绍及操作大全》:本文主要介绍c++中的set容器介绍及操作大全,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录​​一、核心特性​​️ ​​二、基本操作​​​​1. 初始化与赋值​​​​2. 增删查操作​​​​3. 遍历方

解析C++11 static_assert及与Boost库的关联从入门到精通

《解析C++11static_assert及与Boost库的关联从入门到精通》static_assert是C++中强大的编译时验证工具,它能够在编译阶段拦截不符合预期的类型或值,增强代码的健壮性,通... 目录一、背景知识:传统断言方法的局限性1.1 assert宏1.2 #error指令1.3 第三方解决

C++11委托构造函数和继承构造函数的实现

《C++11委托构造函数和继承构造函数的实现》C++引入了委托构造函数和继承构造函数这两个重要的特性,本文主要介绍了C++11委托构造函数和继承构造函数的实现,具有一定的参考价值,感兴趣的可以了解一下... 目录引言一、委托构造函数1.1 委托构造函数的定义与作用1.2 委托构造函数的语法1.3 委托构造函

C++11作用域枚举(Scoped Enums)的实现示例

《C++11作用域枚举(ScopedEnums)的实现示例》枚举类型是一种非常实用的工具,C++11标准引入了作用域枚举,也称为强类型枚举,本文主要介绍了C++11作用域枚举(ScopedEnums... 目录一、引言二、传统枚举类型的局限性2.1 命名空间污染2.2 整型提升问题2.3 类型转换问题三、C

解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘问题

《解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘问题》:本文主要介绍解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4... 目录未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘打开pom.XM