动态规划(Dynamic Programming)(dp)描述。
类型1:
类型2:依据力扣分类
类型3:
非线性树型如:337.打家劫舍 III - 力扣(LeetCode)——中等
算法往往为多重循环而不容易模拟/举例推导dp数组以验证。可在调试时再验证。
509.斐波那契数 - 力扣(LeetCode)——简单的题解:
class Solution
{
public:
int fib(int n)
{
if (n == 0 || n == 1)
{
return n;
}
// 1. 确定dp数组中下标和值的含义 -> 确定dp数组的维度、规模和所求结果/返回值
vector<int> dp(n + 1);
// 3. 确定dp数组的初始化值
dp[0] = 0;
dp[1] = 1;
// 4. 确定dp数组的遍历顺序和临界条件
for (int i = 2; i <= n; ++i)
{
// 2. 确定dp数组的状态转移方程/递推公式
dp[i] = dp[i - 1] + dp[i - 2];
}
// 1. 确定dp数组中下标和值的含义 -> 确定dp数组的维度、规模和所求结果/返回值
return dp[n];
}
// 5. 模拟/举例推导dp数组以验证
};
作用:降低时间复杂度,增加空间复杂度
描述:
可能的优化:O(n²) -> O(n)。n为数据规模
作用:优化空间复杂度
描述:
可能的优化:O(n) -> O(1);O(m × n) -> O(n)
混合背包:
分组背包:依组装包,每组数量为一
背包:容量
物品:体积,价值,数量
详细解释参见:代码随想录 (programmercarl.com)
二维dp数组:
#include
#include
using std::vector;
using std::cout;
using std::endl;
void func()
{
// 0. 分析应用问题转换为背包问题 -> 确定背包问题的参数
int capacity = 4; // 背包:容量
vector<int> weight = {1, 3, 4}; // 物体:重量
vector<int> value = {15, 20, 30}; // 物体:价值
int quantity = 3; // 物体:数量
// 1. 确定dp数组中下标和值的含义 -> 确定dp数组的维度、规模和所求结果/返回值
vector<vector<int>> dp(quantity, vector<int>(capacity + 1));
// 3. 确定dp数组的初始化值
for (int j = 0; j < weight[0]; ++j)
{
dp[0][j] = 0;
}
for (int j = weight[0]; j <= capacity; ++j)
{
dp[0][j] = value[0];
}
// 4. 确定dp数组的遍历顺序和临界条件
for (int i = 1; i < quantity; ++i)
{
// 相关处理
for (int j = 0; j <= capacity; ++j)
{
// 2. 确定dp数组的状态转移方程/递推公式
if (j < weight[i])
{
dp[i][j] = dp[i - 1][j];
}
else
{
dp[i][j] = std::max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
}
}
}
// 1. 确定dp数组中下标和值的含义 -> 确定dp数组的维度、规模和所求结果/返回值
cout << dp[quantity - 1][capacity] << endl; // 35
}
// 5. 模拟/举例推导dp数组以验证
int main()
{
func();
}
二维dp数组可先遍历物品,后正序遍历背包:
// 4. 确定dp数组的遍历顺序和临界条件
for (int i = 1; i < quantity; ++i)
{
// 相关处理
for (int j = 0; j <= capacity; ++j)
{
// 2. 确定dp数组的状态转移方程/递推公式
if (j < weight[i])
{
dp[i][j] = dp[i - 1][j];
}
else
{
dp[i][j] = std::max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
}
}
}
二维dp数组可先遍历背包,后正序遍历物品:
// 4. 确定dp数组的遍历顺序和临界条件
for (int j = 0; j <= capacity; ++j)
{
// 相关处理
for (int i = q; i <= quantity; ++i)
{
// 2. 确定dp数组的状态转移方程/递推公式
if (j < weight[i])
{
dp[i][j] = dp[i - 1][j];
}
else
{
dp[i][j] = std::max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
}
}
}
一维dp数组只能先遍历物品,后倒序遍历背包:
#include
#include
using std::cout;
using std::endl;
using std::vector;
void func()
{
// 0. 分析应用问题转换为背包问题 -> 确定背包问题的参数
int capacity = 4; // 背包:容量
vector<int> weight = {1, 3, 4}; // 物体:重量
vector<int> value = {15, 20, 30}; // 物体:价值
int quantity = 3; // 物体:数量
// 1. 确定dp数组中下标和值的含义 -> 确定dp数组的维度、规模和所求结果/返回值
// 3. 确定dp数组的初始化值
vector<int> dp(capacity + 1, 0);
// 4. 确定dp数组的遍历顺序和临界条件
for (int i = 0; i < quantity; ++i)
{
// 相关处理
for (int j = capacity; j >= weight[i]; --j)
{
// 2. 确定dp数组的状态转移方程/递推公式
dp[j] = std::max(dp[j], dp[j - weight[i]] + value[i]);
}
}
// 1. 确定dp数组中下标和值的含义 -> 确定dp数组的维度、规模和所求结果/返回值
cout << dp[capacity] << endl; // 35
}
// 5. 模拟/举例推导dp数组以验证
int main()
{
func();
}
注意:还存在三维或更高维dp数组。如:474.一和零 - 力扣(LeetCode)——中等
详细解释参见:代码随想录 (programmercarl.com)
一维dp数组:
#include
#include
using std::cout;
using std::endl;
using std::vector;
void func()
{
// 0. 分析应用问题转换为背包问题 -> 确定背包问题的参数
int capacity = 4; // 背包:容量
vector<int> weight = {1, 3, 4}; // 物体:重量
vector<int> value = {15, 20, 30}; // 物体:价值
int quantity = 3; // 物体:数量
// 1. 确定dp数组中下标和值的含义 -> 确定dp数组的维度、规模和所求结果/返回值
// 3. 确定dp数组的初始化值
vector<int> dp(capacity + 1, 0);
// 4. 确定dp数组的遍历顺序和临界条件
for (int i = 0; i < quantity; ++i)
{
// 相关处理
for (int j = weight[i]; j <= capacity; ++j)
{
// 2. 确定dp数组的状态转移方程/递推公式
dp[j] = std::max(dp[j], dp[j - weight[i]] + value[i]);
}
}
// 1. 确定dp数组中下标和值的含义 -> 确定dp数组的维度、规模和所求结果/返回值
cout << dp[capacity] << endl; // 60
}
// 5. 模拟/举例推导dp数组以验证
int main()
{
func();
}
一维dp数组可先遍历物品,后正序遍历背包:
// 4. 确定dp数组的遍历顺序和临界条件
for (int i = 0; i < quantity; ++i)
{
// 相关处理
for (int j = weight[i]; j <= capacity; ++j)
{
// 2. 确定dp数组的状态转移方程/递推公式
dp[j] = std::max(dp[j], dp[j - weight[i]] + value[i]);
}
}
一维dp数组可先遍历背包,后正序遍历物品:
// 4. 确定dp数组的遍历顺序和临界条件
for (int j = 0; j <= capacity; ++j)
{
// 相关处理
for (int i = 0; i < quantity; ++i)
{
// 2. 确定dp数组的状态转移方程/递推公式
if (j - weight[i] >= 0)
{
dp[j] = std::max(dp[j], dp[j - weight[i]] + value[i]);
}
}
}
若求排列数,则先遍历背包,后遍历物品
若求组合数,则先遍历物品,后遍历背包
详细解释参见:代码随想录 (programmercarl.com
题目组织依据:代码随想录 (programmercarl.com)
基础问题:
背包问题:0-1背包问题
背包问题:完全背包问题
打家劫舍问题:
股票问题:
子序列问题:非连续和连续子序列问题
子序列问题:编辑距离问题
子序列问题:回文问题
动态规划(Dynamic Programming)(dp)描述。