动态规划通常可以拆解为五步,
- 确定dp数组下标的含义
- 确定状态转换方程
- dp数组如何初始化
- 确定遍历顺序
- 举例推到dp数组
典型问题DP
0-1背包问题
有N件物品和一个最多能被重量为W 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大.
问题本质:
每一件物品其实只有两个状态,取或者不取。
暴力法:回溯。
时间复杂度:O(2^n)
DP思路:
- 确定dp数组以及下标的含义。
二维数组,dp[i][j] 表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少
- 确定递推公式。
存在两个状态, 即放i物品和不放i物品
不放该物品,dp[i-1][j].
放该物品,dp[i-1][j-weight[i]] + value[i]
- 初始化数组
可以根据引索来初始化,i-1>=0; i >=1
所以i = 0的全部元素需要初始化。
j = 0时,不能放任何东西,dp[i][0] = 0;
- 确定遍历顺序
如上图所示的数组,先遍历行(物品),更好理解。
- 举例推导dp数组
即自己手动模拟填完该数组
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main(){
// 3个物品; 背包重量4
vector<int> weight = {0,1,3,4};
vector<int> value = {0,15,20,30};
vector<vector<int>> dp(3, vector<int>(5,0));
// 初始化
for(int i = 1; i < 4; i++){
dp[0][i] = value[0];
}
for(int i = 1; i < 3; i++){
for(int j = 1; j < 5; j++){
// 转移方程
if(j - weight[i] < 0){
dp[i][j] = dp[i-1][j];
}else{
dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]] + value[i]);
}
}
}
cout<< dp[2][4] <<endl;
system("pause");
return 0;
}