目录:
1.01背包(朴素版,朴素版2,一维滚动数组优化)
2.完全背包问题(朴素版,优化1,一维优化)
3.多重背包问题(朴素版,二分优化)
一.01背包问题
问题简述:有m个物品每个物品有对应价值ve[i]和对应体积t[i],求在有限背包容量te里能装的最大价值为多少
应该是最基础的背包问题了,核心代码dp[i][j]=dp[i-1][j-v[i]]+w[i];
思路:枚举物品并且枚举背包空间,讨论放与不放;
(1)朴素版1
//#iclude<bits/stdc++.h>
//using namespace std;
//
//const int N=1e2+5;
//const int N1=1e3+5;
//int t[N];//体积
//int ve[N];//价值
//int dp[N1][N1];
//int te,m;//背包总体积和物品数量
//
//int main(){
// cin>>te>>m;
// for(int i=1;i<=m;i++){
// cin>>t[i]>>ve[i];//输入每个物品的体积和价值
// }
//
// for(int i=1;i<=m;i++){//枚举物品个数(物品编号)
// for(int j=1;j<=te;j++){//同时枚举空间
//
// if(j>=t[i]){//若放的下
// dp[i][j]=max(dp[i-1][j],dp[i-1][j-t[i]]+ve[i]);
//则把前i个物品放到容量为j 的背包里的结果为,不拿(则把前i-1个物品放到容量为j的背包里 )和//拿(则把前i-1个物品放到容量为j-v[i]的背包里 )+当前物品价值的最大值
// }
// else{
// dp[i][j]=dp[i-1][j];//否则就直接将i-1转移下来
// }
// }
// }
//
// cout<<dp[m][te];
//输出m个物品放到容量为te的背包里的最大价值,因为循环i,j从1开始,所以不用减一
// return 0;
//}
(2)朴素版2
//#include<bits/stdc++.h>
//using namespace std;
//
//const int N=1e2+5;
//const int N1=1e3+5;
//int t[N];
//int ve[N];
//int dp[N1][N1];
//int te,m;
//
//int main(){
// cin>>te>>m;
// for(int i=1;i<=m;i++){
// cin>>t[i]>>ve[i];
// }
//
// for(int i=1;i<=m;i++){
// for(int j=1;j<=te;j++){
// dp[i][j]=dp[i-1][j];//若放前边不写else
// if(j>=t[i]){
// dp[i][j]=max(dp[i][j],dp[i-1][j-t[i]]+ve[i]);//则前边是i,不是i-1
// }
//
// }
// }
//
// cout<<dp[m][te];
// return 0;
//}
(3)一维滚动数组优化
#include<bits/stdc++.h>
using namespace std;
const int N = 1e3 + 5;
int dp[N];
int num,ve;
int n[N],vel[N];
int main(){
cin>>num>>ve;
for(int i=1;i<=num;i++){
cin>>n[i]>>vel[i];
}
for(int i=1;i<=num;i++){
for(int j=ve;j>=n[i];j--){ //倒过来可以不影响前边的,若正过来则要加一个temp数组
dp[j]=max(dp[j],dp[j-n[i]]+vel[i]);//因为每次其实只和i-1比较,所以可以只存i-1行并与前边自 己比较,从而化简为一维数组(省空间,不省时间)
}
}
cout<<dp[ve];
}
二.完全背包
与01背包很相似,只是每个物品有无限个并且可以选无限次。求最大价值。
(1)朴素版(三层循环O(n**3))太慢了需要优化
//#include<bits/stdc++.h>
//using namespace std;
//
//const int N1=1e4+5;
//const int N=1e7+5;
//int t[N1];
//int ve[N1];
//int dp[N1][N];
//int te,m;
//
//int main(){
// cin>>te>>m;
// for(int i=1;i<=m;i++){
// cin>>t[i]>>ve[i];
// }
//
// for(int i=1;i<=m;i++){
// for(int j=1;j<=te;j++){
// for(int k=0;k*t[i]<=j;k++){//枚举选k个从不选到k*t[i]<=j
//
// dp[i][j]=max(dp[i][j],dp[i-1][j-k*t[i]]+k*ve[i]);选k个的dp注意是i不是i-1;
//
// }
//
// }
// }
// cout<<dp[m][te];
// return 0;
//}
(2)完全背包的优化1
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+5;
int t[N];
int ve[N];
int dp[N][N];
int te,m;
int main(){
cin>>te>>m;
for(int i=1;i<=m;i++){
cin>>t[i]>>ve[i];
}
for(int i=1;i<=m;i++){
for(int j=1;j<=te;j++){
dp[i][j]=dp[i][j];
if(j>=t[i]){
dp[i][j]=max(dp[i][j],dp[i][j-t[i]]+ve[i]);//与自己比较,只用将01背包中的i-1改为i即可
}
}
}
cout<<dp[m][te];
return 0;
}
(3)完全背包的优化 2(优化为一维数组,更省空间)
#include<iostream>
using namespace std;
const int N = 1e7 + 5;
int n;
long long W[N], V[N], dp[N];
int main()
{
int m, n;
cin>>m>>n;
for(int i = 1; i <= n; i++) cin>>V[i]>>W[i];
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++)
{
if(j >= V[i])
{
dp[j] = max(dp[j], dp[j-V[i]] + W[i]);
}
}
}
cout<<dp[m];
return 0;
}
三.多重背包问题
与完全背包和01背包很相似,只不过是每个物品有s件(或者说最多选s件),求可装最大价值。
(1)朴素版(还是三层循环,太慢)
//#include<bits/stdc++.h>
//using namespace std;
//const int N = 1e3 + 5;
//int dp[N][N];
//int num,ve;
//int n[N],vel[N],s[N];
//int main(){
// cin>>num>>ve;
// for(int i=1;i<=num;i++){
// cin>>n[i]>>vel[i]>>s[i];
// }
//
// for(int i=1;i<=num;i++){
// for(int j=1;j<=ve;j++){
// dp[i][j]=dp[i-1][j];
// for(int k=1;k*n[i]<=j && k<=s[i];k++){
// dp[i][j]=max(dp[i][j],dp[i-1][j-k*n[i]]+k*vel[i]);//解法类似于完全背包,多加一个循环条件
// }
// }
// }
// cout<<dp[num][ve];
//}
(2)二分优化(重点)
三层循环总是太慢所以要优化。
s为数目,所以可以将其分解为2的k次方的和的形式,然后再将每一项单独打包,从而转换为纯粹意义上的 01背包。
如10个物品A可以拆分为1+2+4+3。3为最后剩下的
图示:A AA AAAA AAA
每一份物品打包合一,变成一个新的大物品,则变换后为:
B C D E 四个大物品,再进行01背包即可;利用这四个物品可以凑出任意在10以内的数字
//多重背包的优化+一维数组(滚动数组)
//#include<iostream> //O(N*log(S)*V)
//#include<cmath>
//using namespace std;
//
//const int N = 105, M = log(N);
//int W[N*M], V[N*M], S[N], dp[N];
//
//int main()
//{
// int n, m;
// cin>>n>>m;
// int cnt = 0;
// for(int i = 1; i <= n; i++)
// {
// int v, w, s;
// cin>>v>>w>>s;
// int k = 1;
// while(k <= s) //分组进行打包
// {
// V[++cnt] = k*v;
// W[cnt] = k*w;
// s -= k;
// k *= 2;
// }
// if(s) //若有剩余就像3则继续打包
// {
// V[++cnt] = s*v;
// W[cnt] = s*w;
// }
// }
//
// 转化为纯粹的01背包
// for(int i = 1; i <= cnt; i++)
// {
// for(int j = m; j >= V[i]; j--)
// {
// // dp[j] = dp[j]
// dp[j] = max(dp[j], dp[j-V[i]] + W[i]);
// }
// }
// cout<<dp[m];
//}
完~~