1.打表
动态规划的核心思想就是通过子问题的解,最终得出最终问题的解,当所有的子问题都解决了,最原始问题的解也就出来了,所以我们经常用一个数组dp来记录子问题的解,每当第一次求解时就把问题的解求出并存起来,当遇到相同问题时就不用重新求解,可以直接复用子问题的解。
这里告诉我们2个关键点,一是可以通过求解子问题的解来得出原问题的解,二是整个dp数组打表完成,答案就在dp数组里面,因为每个子问题都满足最优子结构即每个子问题的解都是最优解
2.最优子结构
动态规划告诉我们,要使用动态规划,必须满足重复子问题和最优子结构,最优子结构就是每个子问题的求解都是最优解。从这里我们可以看出,每个子问题的最优解可能有n种策略得出,但不管有几种策略,得到子问题的解就是所有策略最优的结果
3.无后效性
无后效性就是当子问题的解确定下来了,就一直不会改变,一直是这个结果,不会受到后面的计算而改变该子问题的解。比如dp[5]=5,那么这个子问题的解确定下来之后就不会再改变。
动态规划三步走
1.定义dp数组的含义:要求谁就定义谁
2.根据观察列出状态转移方程
3.找出初始条件
此题目出自算法笔记
1.我们根据第一步,定义dp数组的含义,由于题目要求使得背包内物品的总价值最大,所以我们定义取前i件物品装入容量为v的总价值最大。
2.观察
刚开始,物品为0件,也就没有存放物品之说,所以第一行全部填0,背包容量为0,所以也没有容量,第一列全部填0。
当物品数量为1时,容量为0时,背包放不下,因为第一件物品重量为3,所以背包价值为0,同理,当背包容量为1,2时也是0,背包同样存不下。
当背包容量为3时,根据观察,这里可以选择存下第一个物品,所以此时最优子结构是当背包容量为3时,背包价值为4对吧,那么这个4是怎么的出来的呢?我们暂且不表,因为还比较难看出来,我们只知道当物品重量小于等于背包容量时,可以存下第一个物品,即w[i]<=j时,dp[i][j]=4,因为只有一个物品,所以当背包容量大于3时,都只能存下一个物品,所以背包容量为4-10时,背包最大总价值都为4。
当物品数量为2时,背包容量0-2都满足不了存放任何一个物品,所以背包总价值为0,当背包容量为3时,能存下第一个物品,但是存不下第二个物品对吧?所以此时最大的背包价值就是上一个物品的最大背包价值,即当w[i]>j(第二个物品重量大于背包容量时),最优的选择就是选择上一个物品,此时最大价值为4。此时可以列出第一个dp转移方程,dp[i][j]=dp[i-1][j]。同理当背包容量为4时,只能选择上一个物品,最大价值为4。
当背包容量为5时,我们可以选择2个物品,第一个物品重量为3能被选择,第二个物品重量为5也能选择,那么选择哪个价值更大呢?即可以说第二个物品选还是不选的问题,因为选和不选都有可能使背包价值更大,为比较2种选择的最大值。当不选择时,就是选择上一个物品dp[i-1][j],最大价值为4,当选择时,最大价值就是5,经过肉眼观察比较我们得出选择第二个物品为最优解。选择时我们要减去背包容量,同时加上第二个物品的价值。此时我们可以列出状态转移方程,即w[i]<=j(第二个物品的重量小于背包容量),dp[i][j]=max(dp[i-1][j](不选择),dp[i-1][j-w[i]]+v[i](选择)),可以带入试试,当背包容量为5时,选择第二个物品时dp[1][0]+5表示,第一个物品不选,选择第二个物品。因为dp[1][0]为0嘛,表示在只有一个物品且容量为0时,背包最大值为0。在只有一个物品遇见过的时候已经记录过答案了,直接复用。同理,当背包容量为6-7时,都是选择第二个,背包价值最大为5,
当背包容量为8时,此时2个物品都能放下,即w[i]<=j(第二个物品小于背包容量),dp[i][j]=dp[i-1][j-w[i]]+v[i],合并到dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i],由于都是要选择第二个,dp[i-1][j-w[i]]+v[i]的值始终要大于dp[i-1][j],所以可以合并。带入看看,当背包容量为8,选择第二个物品时dp[1][4]+5,而dp[1][4]我们在物品只有一个时已经记录过了,dp[1][4]=4,所以背包最大值为4+5=9。
至此,所有的条件已经找齐了
列出状态转移方程
当w[i]>j时,dp[i][j]=dp[i-1][j];
当w[i]<=j时,dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
3.找出初始条件,即当有0件物品可以选时,第一行全为0,当背包容量为0时,第一列全为0。因为创建数组时默认全部赋值为0,所以初始条件可以忽略掉
输入
5 8
3 5 1 2 2
4 5 2 1 3
完整代码
#include <cstdio>
#include <iostream>
#include <queue>
#include <cstring>
#include <cmath>
using namespace std;
int n,v; //有几件物品,背包容量
int w[100]; //重量
int c[100]; //价值
int dp[100][100]; //dp数组
int main(){
//输入
cin>>n>>v;
for(int i=1; i<=n; i++){ //输入重量
cin>>w[i];
}
for(int i=1; i<=n; i++){ //输入价值
cin>>c[i];
}
/*因为创建数组时默认全部赋值为0,所以初始条件可以忽略掉。*/
//dp
for(int i=1; i<=n; i++){ //从第1件物品开始选
for(int j=1; j<=v; 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]]+c[i]);
}
}
}
cout<<dp[n][v]; //输出最大值
return 0;
}
输出