目录
问题描述:
有N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
测试样例:
n=4,{w,v}={(2,3),(1,2),(3,4),(2,2)},W=5
4
2 3
1 2
3 4
2 2
5
1.纯递归解决,容易溢出
#include <iostream>
#include <cstring>
using namespace std;
const int MAX_N=1000;
int n,W;
int w[MAX_N],v[MAX_N];
int dp[MAX_N+1][MAX_N+1];//记忆化数组
//从第i个物品开始挑选总重小于j的部分
int rec(int i,int j){
int res;//剩余物品
if(i==n){//已经没有剩余物品了
res=0;
}else if(j<w[i]){//无法挑选这个物品
res=rec(i+1,j);
}else{//尝试挑选和不挑选两种情况
res=max(rec(i+1,j),rec(i+1,j-w[i])+v[i]);
}
//将结果记录在数组中
return res;
}
int main(){
//-1表示未计算过,初始化整个数组
memset(dp,-1,sizeof(dp));
cin>>n;
for(int i=0;i<n;i++){
cin>>w[i]>>v[i];
}
cin>>W;
cout<<rec(0,W)<<endl;
return 0;
}
2.记忆化搜索法
#include <iostream>
#include <cstring>
using namespace std;
const int MAX_N=1000;
int n,W;
int w[MAX_N],v[MAX_N];
int dp[MAX_N+1][MAX_N+1];//记忆化数组
//从第i个物品开始挑选总重小于j的部分
int rec(int i,int j){
if(dp[i][j]>=0){//如果已经计算过的话就直接使用之前的结果
return dp[i][j];
}
int res;//剩余物品
if(i==n){//已经没有剩余物品了
res=0;
}else if(j<w[i]){//无法挑选这个物品
res=rec(i+1,j);
}else{//尝试挑选和不挑选两种情况
res=max(rec(i+1,j),rec(i+1,j-w[i])+v[i]);
}
//将结果记录在数组中
return dp[i][j]=res;
}
int main(){
//-1表示未计算过,初始化整个数组
memset(dp,-1,sizeof(dp));
cin>>n;
for(int i=0;i<n;i++){
cin>>w[i]>>v[i];
}
cin>>W;
cout<<rec(0,W)<<endl;
return 0;
}
3.由记忆化搜索推导递推关系,使用动态规划法DP
#include <iostream>
#include <cstring>
using namespace std;
const int MAX_N=1000;
int n,W;
int w[MAX_N],v[MAX_N];
int dp[MAX_N+1][MAX_N+1];//记忆化数组
int main(){
//-1表示未计算过,初始化整个数组
memset(dp,-1,sizeof(dp));
cin>>n;
for(int i=0;i<n;i++){
cin>>w[i]>>v[i];
}
cin>>W;
for(int i=n-1;i>=0;i--){
for(int j=0;j<=W;j++){
if(j<w[i]){
dp[i][j]=dp[i+1][j];
}else{
dp[i][j]=max(dp[i+1][j],dp[i+1][j-w[i]]+v[i]);
}
}
}
cout<<dp[0][W]<<endl;
return 0;
}
备注:
1.递推关系可以有很多种,此题至少三种
2.memset用法:https://blog.csdn.net/qq_41895747/article/details/103898388