CSP初赛复习-28-动态规划-完全背包
背包问题
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
输出样例
10
动态规划 -二维数组朴素算法 三层循环
大致思路
数据量最大1000会超时,主要理解思路
1 问题分解,求N件物品不超过容积V的最大价值,转化为f[i] [j] i件物品不超过体积j的所有集合,所有状态
转换为求f[i] [j] i件物品不超过体积j的最大价值(1<=i<=N,0<=j<=V)
2 第外层循环遍历N件物品,内层循环遍历1~V的整数体积,f[i] [j] 表示前i件物品最近体积为j时的最大价值
3 每件物品可以填充多个,因此需要找出放入1~t/v[i]件i物品时最大体积,三重循环
4 考虑前i件物品时,把前i件物品组合的集合分成2部分,不加i的最大价值和i的最大价值
不加i的最大价值 dp[i-1] [ j-k*v[i]]
i物品的最大价值 k*w[i] (k可以时0个或者多个i物品)
参考程序
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
int v[N],w[N];//v[i]保存每件物品的体积 w[i]保存每件物品重量
int dp[N][N];//f[i][j]前i件物品,承重不超过j的最大价值
int main() {
int N,V;
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=1;j<=V;j++) {//遍历每个整数的体积
int t=j/v[i];//背包可以装t个体积为v[i]物品
for (int k=0;k<=t;k++){//0 1 2 3...t 逐个装入背包
//类似01背包 装和不装取最大 装包括装1个装2个(包含01背包)
//k=0时dp[i-1][j-k*v[i]]+k*w[i] -- dp[i-1][j]
dp[i][j]=max(dp[i][j],dp[i-1][j-k*v[i]]+k*w[i]);
}
}
}
cout<<dp[N][V];//装N件物品在不超过体积V的最大价值
return 0;
}
动态规划 -二维数组朴素算法 双重循环优化
大致思路
1 问题分解,求N件物品不超过容积V的最大价值,转化为f[i] [j] i件物品不超过体积j的所有集合,所有状态
转换为求f[i] [j] i件物品不超过体积j的最大价值(1<=i<=N,0<=j<=V)
2 第外层循环遍历N件物品,内层循环遍历1~V的整数体积,f[i] [j] 表示前i件物品最近体积为j时的最大价值
3 i个物品装入体积j的背包产生的集合分为2部分:1不包括i和2包括1个或多个i

不包括i时
i个背包不超过体积j的最大价值f[i] [j]=f[i-1] [j]
包括i时
可能包括1个i,包括2个i包括3个i...
对应i个物品装入j体积背包最大价值如下1式:
f[i] [j]= max(f[i-1] [j-v]+w,f[i-1] [j-2 * v]+2 * w,f[i-1] [j-3 * v]+3 * w ...)
又对应i个物品装入j-v体积背包最大价值如下2式:
f[i] [j-v]=max(f[i-1] [j-v] ,f[i-1] [j-2 * v]+ w ,f[i-1] [j-3 * v]+2 * w ...)
1式每个元素都比2式大w,对应的max也大w
因此f[i] [j] = f[i] [j-v]+w
综合上述不包括i和包括i 2种情况,得出完全背包的动态转移方程如下:
f[i] [j] = max(f[i-1] [j],f[i] [j-v]+w)
参考程序
#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][j-v[i]]+w[i]);//不选i和选择i集合2部分取最大
}
}
}
cout<<f[N][V];
return 0;
}
一维滚动数组优化
1 二维完全背包问题的等价变形
2 i个物品装入体积j的背包产生的集合分为2部分:1不包括i和2包括1个或多个i

不包括i时
i个背包不超过体积j的最大价值f[i] [j]=f[i-1] [j]
f[i] [j]=f[i-1] [j] 还未变化可以转变为f[j]=f[j] 可以省略
包括i时
可能包括1个i,包括2个i包括3个i...
对应i个物品装入j体积背包最大价值如下1式:
f[i] [j] = max(f[i-1] [j],f[i] [j-v]+w) 此时 f[i] [j-v]是i个物品时体积j-v的最大值,需要提前计算,因此需要从左到右循环
01背包是f[i-1] [j-v] 是i-1个物品时体积j-v的最大值,是i-1个物品的,不能被第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[i];j<=V;j++){//遍历每个整数的体积
f[j]=max(f[j],f[j-v[i]]+w[i]);//不选i和选择i集合2部分取最大
}
}
cout<<f[V];
return 0;
}
CSP初赛复习-28-动态规划-练习题
https://www.cnblogs.com/myeln/articles/17626730.html
作者:newcode 更多资源请关注纽扣编程微信公众号

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

浙公网安备 33010602011771号