例题1:01背包
1267:【例9.11】01背包问题
时间限制: 1000 ms 内存限制: 65536 KB
提交数: 34747 通过数: 20968
【题目描述】
一个旅行者有一个最多能装 MM 公斤的背包,现在有 nn 件物品,它们的重量分别是W1,W2,...,WnW1,W2,...,Wn,它们的价值分别为C1,C2,...,CnC1,C2,...,Cn,求旅行者能获得最大总价值。
【输入】
第一行:两个整数,MM(背包容量,M<=200M<=200)和NN(物品数量,N<=30N<=30);
第2..N+12..N+1行:每行二个整数Wi,CiWi,Ci,表示每个物品的重量和价值。
【输出】
仅一行,一个数,表示最大总价值。
【输入样例】
10 4
2 1
3 3
4 5
7 9
【输出样例】
12
【题目解析】
基本思路:
状态数组的定义:
dp[i][j],表示选第i个物品时,在j空间下所获取的最大价值是多少。
阶段:
以物品为阶段,1—n个物品。
决策:
即在第i个物品时,选或不选。
边界条件:
因为要取最大值,把dp数组定义为全局变量,即为0.
状态转移方程:
dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]]+w[i]); dp[i][j]继承的是dp[i-1][j]的值,在目前空间大于目前所选物品所需要空间的情况下
代码参考:
#include<iostream>
using namespace std;
int v[210],w[35];
int dp[35][210];
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=0;j<=m;j++){
dp[i][j]=dp[i-1][j];
if(j>=v[i]){
dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]]+w[i]);
}
}
}
cout<<dp[n][m];
return 0;
}
代码可以再次进行优化,用一维的dp数组进行操作,只需要保证dp[j]能够继承到dp[j-v[i]]的值,由此可以将第二层for循环倒叙枚举空间即可。
【代码参考】
#include<iostream>
using namespace std;
int m,n;
int w[35],v[35];
int dp[210];
int main(){
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=m;j>=v[i];j--){
dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
}
}
cout<<dp[m];
return 0;
}
例题2:完全背包
1268:【例9.12】完全背包问题
时间限制: 1000 ms 内存限制: 65536 KB
提交数: 28446 通过数: 15243
【题目描述】
设有nn种物品,每种物品有一个重量及一个价值。但每种物品的数量是无限的,同时有一个背包,最大载重量为MM,今从nn种物品中选取若干件(同一种物品可以多次选取),使其重量的和小于等于MM,而价值的和为最大。
【输入】
第一行:两个整数,MM(背包容量,M≤200M≤200)和NN(物品数量,N≤30N≤30);
第2..N+12..N+1行:每行二个整数Wi,CiWi,Ci,表示每个物品的重量和价值。
【输出】
仅一行,一个数,表示最大总价值。
【输入样例】
10 4
2 1
3 3
4 5
7 9
【输出样例】
max=12
题目解析:
做法思路大体与01背包相似,只需向内再加一层for循环枚举第i间物品在当前空间内可以取到的件数即可。
【代码参考】
#include<iostream>
using namespace std;
int m,n;
int v[35],w[35];
int dp[35][210];
int main(){
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=0;j<=m;j++){
dp[i][j]=dp[i-1][j];
for(int k=1;k<=m/v[i];k++){
if(j>=k*v[i]){
dp[i][j]=max(dp[i][j],dp[i-1][j-k*v[i]]+k*w[i]);
}
}
}
}
cout<<"max="<<dp[n][m];
return 0;
}
同样也有一维优化,第二层for循环从v[i]开始,枚举到所能承受的最大空间,每次d[j]都继承d[j-v[i]]的值即可。
【代码参考】
#include<iostream>
using namespace std;
int v[210],w[35];
int dp[210];
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=v[i];j<=m;j++){
dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
}
}
cout<<"max="<<dp[m];
return 0;
}
例题3:多重背包
4. 多重背包问题 I
有 NN 种物品和一个容量是 VV 的背包。
第 ii 种物品最多有 sisi 件,每件体积是 vivi,价值是 wiwi。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。
输入格式
第一行两个整数,N,VN,V,用空格隔开,分别表示物品种数和背包容积。
接下来有 NN 行,每行三个整数 vi,wi,sivi,wi,si,用空格隔开,分别表示第 ii 种物品的体积、价值和数量。
输出格式
输出一个整数,表示最大价值。
数据范围
0<N,V≤1000<N,V≤100
0<vi,wi,si≤1000<vi,wi,si≤100
输入样例
4 5
1 2 3
2 4 1
3 4 3
4 5 2
输出样例:
10
【题目解析】
相对于完全背包来说,每件物品可以选择的数量是有限的,做法与完全背包相似。
【代码参考】(朴素版本)
#include<iostream>
using namespace std;
const int N=110;
int m,n;
int v[N],w[N],dp[N][N],s[N];
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>v[i]>>w[i]>>s[i];
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
dp[i][j]=dp[i-1][j];
for(int k=1;k<=s[i];k++){
if(k*v[i]<=j){
dp[i][j]=max(dp[i][j],dp[i-1][j-k*v[i]]+k*w[i]);
}
}
}
}
cout<<dp[n][m];
return 0;
}
也可以按以上方法进行一维优化
【代码参考】
#include<iostream>
using namespace std;
const int N=1100;
int m,n;
int v[N],w[N],dp[N],s[N];
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>v[i]>>w[i]>>s[i];
}
for(int i=1;i<=n;i++){
for(int j=m;j>=v[i];j--){
for(int k=1;k<=s[i];k++){
if(k*v[i]<=j){
dp[j]=max(dp[j],dp[j-k*v[i]]+k*w[i]);
}
}
}
}
cout<<dp[m];
return 0;
}
当然,也可以将多重背包转化为01背包,进行二进制优化,如:
当输入第i行时数据为3 5 7时,表示此物品每件的体积为3,价值为5,数量为7,
则可将7拆分成
1 2 4
即
3 5 1
6 10 2
12 20 3
将其分别存储起来,按照解01背包的方法进行处理,即可达到优化运算的效果。
【代码参考】
#include<iostream>
using namespace std;
const int N=2e5+5;
int m,n;
int v[N],w[N],dp[N];
int main(){
cin>>n>>m;
int l=0;
for(int i=1;i<=n;i++){
int a,b,c;
cin>>a>>b>>c;
int k=1;
while(c>=k){
l++;
v[l]=a*k;
w[l]=b*k;
c=c-k;
k+k*2;
}
if(c>0){
v[++l]=a*c;
w[l]=b*c;
}
}
for(int i=1;i<=l;i++){
for(int j=m;j>=v[i];j--){
dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
}
}
cout<<dp[m];
return 0;
}