1.1介绍
背包问题属于比较经典的动态规划问题,此处只记录了比较基础的三类,供初学者参考练习。较为复杂的背包问题可以阅读dd大牛的《背包九讲》,讲解的比较详细,当然也有一定的难度,也不需要一开始就要求自己全部精通,学习应量力而行,循序渐进。1.2 01背包问题
1.2.1问题及其解析
问题:一个背包总容量为V,现在有N个物品,第i个 物品体积为weight[i],价值为value[i],现在往背包里面装东西,怎么装能使背包的内物品价值最大?(1) 为何称为01背包问题?所谓01其实就是对于某个物品选还是不选,所以下次再听见“To be or not to be”就应该想到,这不是哲学问题而是个01问题(这大概属于Coder的特殊脑回路)。
(2) 假设A是N个物品之一,那么怎么确定选不选A呢?
(3) 首先,A的体积要小于V;
(4) 其次,加入后A的整体价值要大于没加入时的整体价值;
(5) 运用动态规划的思想,V0=1。。。。。。V-1,最后V0=V时,是基于某一个V0对应的体积和整体价值。
(6) 某一个V0是哪一个V0?V0=V-Weight[i]。
(7) 也就得到该方程:Dp[i][j]=Max(Dp[i-1][j],Dp[i-1][j-Weight[i]]+Vlaue[i]);
1.2.2源码
此处使用定值!
#include<iostream>
using namespace std;
#define V 10
#define N 5
#define max(a,b) (a)>(b)?(a):(b)
double Dp[N+1][V+1];
int main(){
int Weight[N+1]={0,2,3,1,2,5}; //第一个不用
int Value[N+1]={0,2,2,4,5,1};
int i,j;
for(i=1;i<=N;i++){
for(j=1;j<=V;j++){
if(Weight[i]<=j){
Dp[i][j]=max(Dp[i-1][j],Value[i]+Dp[i-1][j-Weight[i]]);
}
else
Dp[i][j]=Dp[i-1][j];
}
}
cout <<Dp[N][V];
return 0;
}
注释:一种优化的解法
(1) 这里简化成了一维数组Dp。
(2) 求Dp[i]时只需知道上一个(Dp[i-1])是什么状态。
(3) 最直观的方法是设断点去看Dp数组的值,其实5次大循环,对应上述二维数组是Dp[i]=Dp[i][],对照着小面给出的结果就很明了了。
(4) 为什么内循环是倒叙?因为需要确保Dp[i][j]的值来源于Dp[i-1][j-Weight[i]]。不会更改到小于Weight[i]的值。
#include<iostream>
using namespace std;
#define V 10
#define N 5
#define max(a,b) (a)>(b)?(a):(b)
double Dp[V+1]//全局变量,自动初始化为0
int main(){
int Weight[N+1]={0,2,3,1,2,5}; //第一个不用
int Value[N+1]={0,2,2,4,5,1};
int i,j;
for (int i=1; i<=N; i++) {
for (int j=V; j>=1; j--)
{
if (Weight[i]<=j)
{
Dp[j]=max(Dp [j],Dp[j-Weight[i]]+Value[i]);
}
}
}
cout<< Dp [V]<<endl;//输出最优解
return 0;
}
1.2.3 结果
行开头为:行号(重量,价值),列为1到V的变化
1.3完全背包问题
1.3.1解析
问题:有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的体积为weight[i],价值为value[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
(1) 与01背包问题不同,这次某个物品不只一个,而是有无限个。
(2) 之前是Dp[i][j]=Max(Dp[i-1][j],Dp[i-1][j-Weight[i]]+Vlaue[i]);而现在应该是Dp[i][j]=Max(Dp[i-1][j],Dp[i][j-Weight[i]]+Vlaue[i]);
(3) 为什么?因为有无限个,不需要再去讨论它之前有没有放入过了,而01的想法是这次放入那么意味着上次没有这个,所以 Dp[i-1]。
1.3.2源码
#include<iostream>
using namespace std;
#define V 10
#define N 5
#define max(a,b) (a)>(b)?(a):(b)
double Dp[N+1][V+1];
int main(){
int Weight[N+1]={0,2,3,1,2,5}; //第一个不用
int Value[N+1]={0,2,2,4,5,1};
int i,j;
for(i=1;i<=N;i++){
for(j=1;j<=V;j++){
if(Weight[i]<=j){
Dp[i][j]=max(Dp[i][j],Value[i]+Dp[i][j-Weight[i]]);
}
}
}
cout <<Dp[N][V];
return 0;
}
注释:一种优化的解法
(1) 内循环在01背包问题优化的情况下改成顺序循环即可
(2) 为什么?不需要确保Dp[i][j]的值来基于Dp[i-1][j-Weight[i]]
#include<iostream>
using namespace std;
#define V 10
#define N 5
#define max(a,b) (a)>(b)?(a):(b)
double Dp[V+1]
//全局变量,自动初始化为0
int main(){
int Weight[N+1]={0,2,3,1,2,5}; //第一个不用
int Value[N+1]={0,2,2,4,5,1};
int i,j;
for (int i=1; i<=N; i++)
for (int j=1; j<=V; j++)
{
if (Weight[i]<=j)
{
f[j]=max(f[j],f[j-Weight[i]]+Value[i]);
}
}
cout<<f[V]<<endl;//输出最优解
return 0;
}
1.3.4 结果
1.4总结
如果不明白的可以多跑跑断点看看,自己多设置几种情况,看看结果有什么区别,或者对照着结果表多推到一下,然后再去想代码的事情。这里是一些浅薄的想法,讲解不清或是谬误,还请指教和包容。