首先我有n个物品,每个物品的体积为w,价值为v,我的背包容量为bagw,问该如何装才能让背包容量最大?
动态规划的思想就是把大问题拆分成小问题,通过寻找大问题与小问题的递推关系,解决一个个小问题(最优解),最终达到解决原问题的目的。但动态规划强的在于,具有记忆性,其实也没那么高深莫测,说白了就是填表,只要根据题意找出状态转移方程,填表就完事。 通过填写表把解决过的子问题答案记录下来,新问题中遇到子问题直接提取,避免了重复计算,节约时间。
之所以叫01背包问题,就是说当我面对第i件物品时,我可以选择装(1)or 不装(0)。 我们建立一个二维数组 V[i][j] , i 是指前i个物品(包括当前物品)最佳组合对应的价值(前i个物品不一定都装),j为当前背包容量。 说通俗点,这个V[i][j]代表的含义也就是面对第i件商品,背包容量为j时所能获得的最大价值。
来,找一下递推关系式,你面对着当前这件物品,无非是 装 或者 不装 ,那么首先要比较一下物品体积和当前背包体积(j)
如果 j<w[i] 那没得谈,装都装不进去 所以 V(i,j)=V(i-1,j)
如果 j>=w(i) 那么我有两种选择,我可以不装也可以装,所以要比较这两种决策导致的结果,选最优,也就是 == V(i,j)=max{V(i-1,j),V(i-1,j-w(i))+v(i)}== (上一个物品可能拿出来也可能接着放在里面)
void findmax(){ //填表
for(int i=1;i<=n;i++){
for(int j=1;j<=bagw;j++){
if(j >= w[i]){
dp[i][j] = max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
}
else dp[i][j] = dp[i-1][j];
}
}
}
关于这个V(i-1,j-w(i))+v(i) 有点难理解,目前我也理解的也有些薄弱,可以这样理解:V[i-1][j]为装到上一个物品在背包j容量时的最佳值,那么如果我要求在j容量的时候放入现在的i物品的价值,那么是不是要先得到容量为(j-weight[i])时候的价值,即先得到 V[i-1][j-weight[i]] ,所以 V[i-1][j-weight[i]]+value[i] 为放入第i物品的价值; V[i-1][j] 就是不放入第i个物品。
填表完成后,我们只能得到最优解时候的价值,但我们不知道他具体都选了哪些物品。所以 ,回溯! 定义一个item[] 数组,初始化0,哪些放进去了哪些置1
具体回溯代码如下:
void traceback(int i,int j){ //回溯瞅瞅哪些放进去了
if(i>=0){
if(dp[i][j]==dp[i-1][j]){
item[i] = 0;
traceback(i-1,j);
}
else if(j-w[i]>=0 && dp[i][j]==dp[i-1][j-w[i]]+v[i]){
item[i] = 1;
traceback[i-1,j-w[i]];
}
}
}
主要思想是寻找状态转移方程然后填表,之后回溯找选中的解,嗯…具体代码如下:
#include<iostream>
#include<algorithm>
using namespace std;
int n; //我有n个物品
int v[150]={0}; //记录我这n个物品每个的价值
int w[150]={0};//记录我这n个物品的体积
int bagw; //记录背包大小
int dp[150][150]={0};//动态规划表
int item[150]={0};//记录最终放入背包了哪些物品
void findmax(){ //填表
for(int i=1;i<=n;i++){
for(int j=1;j<=bagw;j++){
if(j >= w[i]){
dp[i][j] = max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
}
else dp[i][j] = dp[i-1][j];
}
}
}
void traceback(int i,int j){ //回溯瞅瞅哪些放进去了
if(i>=0){
if(dp[i][j]==dp[i-1][j]){
item[i] = 0;
traceback(i-1,j);
}
else if(j-w[i]>=0 && dp[i][j]==dp[i-1][j-w[i]]+v[i]){
item[i] = 1;
traceback[i-1,j-w[i]];
}
}
}
void print(){
for(int i=0;i<n;i++){
for(int j=0;j<bagw;j++){
cout<<dp[i][j]<<' ';
}
cout<<endl;
}
cout<<endl;
for(int i=0;i<n;i++){
cout<<item[i]<<' ';
}
cout<<endl;
}
int main(){
cin>>n;
cin>>bagw;
for(int i=1;i<=n;i++){
cin>>v[i];
}
for(int j=1;j<=n;j++){
cin>>w[j];
}
findmax();
traceback(n,bagw);
print();
return 0;
}