01背包问题
一、本案例的变量
1.w(i) 第i个物品的重量
2.v(i) 第i个物品的价值
3.dp[i] [j] 容量为j的背包在装前i个物品时的最大价值
4.n表示背包的容重量,m表示物品的数量
二、表格
j=0 | j=1 | j=2 | j=3 | j=4 | j=5 | j=6 | j=7 | j=8 | j=9 | j=10 | j=11 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
i=0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
i=1 w=2 v=3 | 0 | 0 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | 3 |
i=2 w=3 v=4 | 0 | 0 | 3 | 4 | 4 | 7 | 7 | 7 | 7 | 7 | 7 | 7 |
i=3 w=4 v=5 | 0 | 0 | 3 | 4 | 5 | 7 | 8 | 9 | 9 | 12 | 12 | 12 |
i=4 w=5 v=6 | 0 | 0 | 3 | 4 | 5 | 7 | 8 | 9 | 10 | 12 | 13 | 14 |
tip:黄颜色的数字有规律,后续可以优化代码
三、算法思路
当j=0 或者 i=0的时候
产生的价值都为0(不管它,不赋值的int数组元素默认值为0)
当j < w[i]时
dp[i] [j] = dp[i-1] [j]
当j >= w[j]时
我们将 拿i号物品的情况 与 不拿i号物品相比较
拿i号物品的情况:v[i] + dp[i-1] [j - w[i]]
不拿i号物品的情况: dp[i-1] [j]
然后选大的
四、代码实现
#include <iostream>
#include <algorithm>
using namespace std;
int dp[32][32];
int w[32],v[32];
int main(){
//n代表物品的数量,m背包的容量
int n,m;
//用户输入物品的数量n 和 背包的容量m
cin>>n>>m;
//给i等于1到n号的重量和价值赋值输入
for(int i = 1;i <= n;i++){
cin>>w[i]>>v[i];
}
//选取dp的最优价值组合
for(int i = 1;i <= n;i++){
for(int j = 1;j <= m;j++){
if(j < w[i])
dp[i][j] = dp[i-1][j];
else
dp[i][j] = max(dp[i-1][j],v[i] + dp[i-1][j-w[i]]);
}
}
//输出最优组合价值表
for(int i = 0;i <= n;i++){
for(int j = 0;j <= m;j++){
cout<<" "<<dp[i][j];
}
cout<<endl;
}
}
五、结果展示
六、持续优化与问题
1.改为单行数组
为什么要倒序
2.最优解回溯
3.可以不从1开始寻得最优解,具体看那篇csdn博客
七、持续优化问题3 最优解的获取‘
item[] 存储最优解的选择
item[0]为0,因为当i=0时,选择与不选择一样,没有任何价值,而实际上也没有价值为0的物品,结构所需而已
思路
当dp[i] [j] 与 dp[i-1] [j]价值相同时,说明没选当前物品,item[i] = 0,并转入考虑dp[i-1] [j]
当dp[i] [j] 满足j >= w[i] 且 dp[i-1] [j - w[i]] + v[i]时,说明选择了当前物品,item[i] = 1,并转入考虑dp[i-1] [j -w[i]]
上面两种方式递归至不再满足i>0为止
实现代码如下
#include <algorithm>
using namespace std;
int dp[32][32];
int w[32],v[32];
int item[32];
void findBest(int i,int j){ //回溯最优解
if(i > 0){
if(dp[i][j] == dp[i-1][j]){
item[i] = 0;
findBest(i-1,j);
}
else if(j - w[i] >= 0 && dp[i][j] == dp[i-1][j - w[i]] + v[i]){
item[i] = 1;
findBest(i-1,j-w[i]);
}
}
}
int main(){
//n代表物品的数量,m背包的容量
int n,m;
//用户输入物品的数量n 和 背包的容量m
cin>>n>>m;
//给i等于1到n号的重量和价值赋值输入
for(int i = 1;i <= n;i++){
cin>>w[i]>>v[i];
}
//选取dp的最优价值组合
for(int i = 1;i <= n;i++){
for(int j = 1;j <= m;j++){
if(j < w[i])
dp[i][j] = dp[i-1][j];
else
dp[i][j] = max(dp[i-1][j],v[i] + dp[i-1][j-w[i]]);
}
}
//输出最优组合价值表
for(int i = 0;i <= n;i++){
for(int j = 0;j <= m;j++){
cout<<" "<<dp[i][j];
}
cout<<endl;
}
int t,k;
cin>>t>>k;
findBest(t,k);
item[0] = 0;
for(int i = 0;i <= n;i++){
cout<<item[i]<<' ';
}
return 0;
}
运行结果如下
ndBest(t,k);
item[0] = 0;
for(int i = 0;i <= n;i++){
cout<<item[i]<<’ ';
}
return 0;
}
运行结果如下
[外链图片转存中...(img-m95sOzDO-1680331764993)]