动态规划
Q:问题的一般形式
A:求最值
Q:你认为动态规划的要素有哪些
A:
-
- 重叠子问题
- 已经计算过结果的问题不用再计算一次,直接用备忘录/DP table来优化穷举。
- 重叠子问题
-
- 最优子结构
- 子问题间必须相互独立,说直白点 当前状态做选择不能受之前的路径影响。比如凑零钱问题中零钱的个数要假设无限的。最优子结构是 根据重叠子问题求得最终问题极值 的必要条件。
- 可以从子问题的最优结果推出更大规模问题的最优结果。
- 比如 在每个班的第一名中就可以 统计出全校第一名,每个班的第一名就是班级这个子结构的 ‘最优解’。
- 比如 使用在每个班内的最大分差 统计不出全校的最大分差,这个问题就不符合最优子结构。
- 最优子结构
-
- 状态转移方程
- 就是穷举方程。
- 状态转移方程
Q:你认为动态规划的核心是什么
A:动态规划的核心应该是先穷举,和找到重叠子问题后对暴力穷举优化。
Q:经典的动态规划问题有哪些
A: 凑零钱问题:
给你 k 种⾯值的硬币, ⾯值分别为 c1, c2 ... ck , 每种硬
币的数量⽆限, 再给⼀个总⾦额 amount , 问你最少需要⼏枚硬币凑出这个
⾦额, 如果不可能凑出, 算法返回 -1 。
int global_count[2000];
int
coinChange (int coins[3], int amount)
{
if (global_count[amount] != 0)
{
return global_count[amount];
}
std::cout << "amount" << amount << std::endl;
if (amount < 0)
{
return -1;
}
if (amount == 0)
{
return 0;
}
int minCount = 65536;
for (int i = 0; i < 3; i++)
{
int tempCount = coinChange (coins, amount - coins[i]);
if (tempCount == -1)
continue;
if (tempCount < minCount)
{
minCount = tempCount;
}
}
if (minCount == 65536)
return -1;
global_count[amount] = minCount + 1;
return minCount + 1;
}
int
main (void)
{
int coins[3] =
{ 1, 2, 5 };
int amount = 1001;
memset (global_count, 0, sizeof(global_count));
int count = coinChange (coins, amount);
std::cout << "count" << count << std::endl;
}
- 1. 暴力递归 : 状态(amount)、选择(coins)、base case(中止条件)
- 2. 带备忘录的递归
- 3. dp数组的迭代解法:从递归变成迭代
Q:一句话概括动态规划
A:聪明的穷举,带备忘录的穷举。
tips: 涉及到递归问题,最好都要画出递归树。对计算复杂度、理清思路非常有帮助。
Q:递归算法的时间复杂度怎么计算?
A:子问题个数乘以解决一个子问题所需要的时间。
Q:未完待续