CSP初赛复习-28-动态规划-01背包
背包问题
01 背包
每种物品只有一个,每件物品只有选与不选两种状态
完全背包
每种物品有无限个
多重背包
每种物品有多个,并且多种物品数量不完全相同
分组背包
按组打包,每组最多一个
01背包
问题描述
现有 N件物品和一个容量为V的背包,第 i件物品的重量是 v[i],价值是 w[i],在背包能承受的范围内,试问将哪些物品装入背包后可使总价值最大,求这个最大价值
输入格式
第一行两个整数,N和V用空格隔开,分别表示物品数量和背包容积 ( 0<=n,m<=1000)
接下来有N行每行两个整数,v[i],w[i]用空格隔开,分别表示第i件物品的体积和价值 ( 0<=v[i],w[i]<=1000)
输出格式
输出一个整数,表示最大价值
输入样例
4 5
1 2
2 4
3 4
4 5
输出样例
8
动态规划 -二维数组朴素算法
大致思路
1 问题分解,求N件物品不超过容积V的最大价值,转化为f[i] [j] i件物品不超过体积j的所有集合,所有状态
转换为求f[i] [j] i件物品不超过体积j的最大价值(1<=i<=N,0<=j<=V)
2 f[i] [j] 每增加一个物品时,比如从i-1到i,对原有集合的影响分为2部分,
对原有集合最大价值无影响 f[i-1] [j] 比如价值太小 或体积超大
对原有集合最大价值有影响 f[i-1] [j-v[i]]+w[i],加入i物品可看做i本身价值+去除i不超过去除i体积v[i]的最大价值
对上面2部分取最大值
3 所以状态转移方程为
f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i])
4 最优子结构
原问题的最优解包含子问题最优解 f [i] [j] 可以通过前面子问题的解计算出来
f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i])
5 重叠子问题
在计算子问题过程中,会出现很多重复使用更小子问题的解
6 无后效性
求解每个子问题的最优解只依赖前面计算好的更小子问题的解,不依赖后面更大问题的解
f[i] [j]不会依赖f[i+1] [j+1]
参考程序
#include<bits/stdc++.h>
using namespace std;
const int maxn=1010;
int N,V;
int v[maxn],w[maxn];//v[i]保存每件物品的体积 w[i]保存每件物品重量
int f[maxn][maxn];//f[i][j]前i件物品,承重不超过j的最大价值
int main(){
cin>>N>>V;//N件物品 V背包容积
for (int i=1;i<=N;i++){//输入每件物品的体积和价值
cin>>v[i]>>w[i];
}
for (int i=1;i<=N;i++){//n件物品
for (int j=0;j<=V;j++){//遍历每个整数的体积
f[i][j]=f[i-1][j];//不选择i物品时和f[i-1][j]最大价值相同
//j<v[i]时f[i-1][j-v[i]]为空集,无意义
if (j>=v[i]){
//选择i时,可看做除去i的最大价值(f[i-1][j-v[i]])+加上i的价值w[i]
f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i]);//不选i和选择i集合2部分取最大
}
}
}
cout<<f[N][V]<<endl;//N件物品装入背包不超过V体积最大价值
return 0;
}
一维滚动数组优化
大致思路
1 由01背包二维数组状态转移方程可知
f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i])
二维表格中,第i行只和i-1行有关
计算第j列只与j列前面的列(j-v[i])有关
因此,可以使用一维数组替换二维数组进行优化,可以提供处理效率
2 可以从右向左更新第i行的数,未更新的左边的列,则是第i-1行对应的数
3 对体积进行遍历时需要保证从大到小
for (int j=V;j>=0;j--)
4 求除去i本身背包可装的最大价值f[j-v[i]]时,需要保证j>v[i]
参考程序
#include<bits/stdc++.h>
using namespace std;
const int maxn=1010;
int N,V;
int v[maxn],w[maxn];//v[i]保存每件物品的体积 w[i]保存每件物品重量
int f[maxn];//f[j]承重不超过j的最大价值
int main(){
cin>>N>>V;//N件物品 V背包容积
for (int i=1;i<=N;i++){//输入每件物品的体积和价值
cin>>v[i]>>w[i];
}
for (int i=1;i<=N;i++){//N件物品
for (int j=V;j>=0;j--){//遍历V~0每个整数的体积
if (j>=v[i]){//j<v[i]为空集,无意义
//选择i时,可看做除去i的最大价值(f[j-v[i]])+加上i的价值w[i]
f[j]=max(f[j],f[j-v[i]]+w[i]);//不选i和选择i集合2部分取最大
}
}
}
cout<<f[V]<<endl;//N件物品装入背包不超过V体积最大价值
return 0;
}
作者:newcode 更多资源请关注纽扣编程微信公众号

从事机器人比赛、机器人等级考试、少儿scratch编程、信息学奥赛等研究学习

浙公网安备 33010602011771号