定义
动态规划是运筹学的一个分支,是求解决策过程最优化的数学方法,把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解。分治算法的基本思想是将一个规模为 N 的问题分解为 K 个规模较小的子问题,这些子问题相互独立且与原问题性质相同。求出子问题的解,就可得到原问题的解。即一种分目标完成程序算法。动态规划是分治思想的延伸,通俗一点来说就是大事化小,小事化无的艺术。
在将大问题化解为小问题的分治过程中,保存对这些小问题已经处理好的结果,并供后面处理更大规模的问题时直接使用这些结果。
本质
对问题状态的定义和状态转移方程的定义(状态以及状态之间的递推关系)
特点
考虑的角度
适用场景
大值/小值, 可不可行, 是不是,方案个数
应用举例
题目描述:
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
方法一:动态规划
//递归
class Solution {
public:
int jumpFloorII(int number) {
if(number <= 0)
{
return 0;
}
else if(number == 1)
{
return 1;
}
else
{
return 2*jumpFloorII(number-1);
}
}
};
//动态规划
class Solution {
public:
int jumpFloorII(int number) {
if(number <= 0)
{
return 0;
}
if(number == 1)
{
return 1;
}
//申请一个数组保存子问题的解
int* record = new int[number-1];
record[0] = 1;
for (int i = 1; i <= number; i++)
{
record[i] = 2*record[i - 1];
}
return record[number-1];
delete[] record;
}
};
//上述解法的空间复杂度为O(n)
//其实f(n)只与它相邻的前一项有关,只需要保存一个子问题的解就可以
//下面方法的空间复杂度将为O(1)
class Solution {
public:
int jumpFloorII(int number) {
if(number <= 0)
{
return 0;
}
if(number == 1)
{
return 1;
}
int fn = 1;
int result = 0;
for (int i = 2; i <= number; i++)
{
result = 2*fn;
fn = result;
}
return result;
}
};
方法二:排列
以台阶为研究对象,除了最后一个台阶只有一种情况,即必须到达,其它台阶都有两种可能性,即青蛙跳到这个台阶或者不跳到这个台阶,所以总的排列数为2^(n-1)。
class Solution {
public:
int jumpFloorII(int number) {
if(number <= 0)
{
return 0;
}
int total = 1;
for(int i = 1;i < number;i++)
{
total *= 2;
}
return total;
}
};
//降低时间复杂度
//上述实现的时间复杂度:O(N)
//O(1)的实现:使用移位操作
class Solution {
public:
int jumpFloorII(int number) {
if(number <= 0)
{
return 0;
}
return 1<<(number-1);
}
};
总结:
此题看似复杂,通过抽象和归纳,可以很容易找出问题的内在规律。
解题的关键是定义问题的状态,以及状态间的递推关系。
扩展1:
上述问题为变态青蛙跳台阶,太疯狂,这只青蛙像是吃了大力丸身上充满了无穷的力量。现在让它变成一个正常的青蛙,限制它一次只能跳1阶或者2阶,又该如何解答?
扩展2:
矩形覆盖,用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用 n 个 2 * 1 的小矩形无重叠地覆盖一个 2 * n 的大矩形,总共有多少种方法?
题目描述:
HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)
方法:动态规划
class Solution {
public:
int FindGreatestSumOfSubArray(vector<int> array) {
if (array.empty())
{
return -1;
}
int sum = array[0];
int maxsum = array[0];
for (int i = 1; i < array.size(); i++)
{
sum = (sum > 0) ? sum + array[i] : array[i];
maxsum = (sum < maxsum) ? maxsum : sum;
}
return maxsum;
}
};
题目描述:
Given a string s and a dictionary of words dict determine if s can be segmented into a space-separated sequence of one or more dictionary words.
For example, given
s =“leetcode”,
dict =[“leet”, “code”]
Return true because"leetcode"can be segmented as"leet code".
(给定一个字符串 s 和一个词典 dict,确定 s 是否可以根据词典中的词分成 一个或多个单词。
比如,给定
s = “leetcode”
dict = [“leet”, “code”]
返回true,因为"leetcode"可以被分成"leet code"。)
方法:动态规划
题目描述:
Given a triangle, find the minimum path sum from top to bottom.
Each step you may move to adjacent numbers on the row below.
For example, given the following triangle
[
[2],
[3,4],
[6,5,7],
[4,1,8,3]
]
The minimum path sum from top to bottom is11(i.e., 2 + 3 + 5 + 1 = 11)
Note:
Bonus point if you are able to do this using only O(n) extra space, where n is the total number of rows in the triangle.
方法一:动态规划
方法二:动态规划(反向思维)
易错点:
只保留每一步的小值,忽略其他路径,造成最终结果错误,局部小不等于全局小。
总结:
遇到关于矩阵,网格,字符串间的比较,匹配的问题,单序列(一维)动态规划解决不了的情况下, 就需要考虑双序列(二维)动态规划。
题目描述:
A robot is located at the top-left corner of a m x n grid (marked ‘Start’ in the diagram below).
The robot can only move either down or right at any point in time.
The robot is trying to reach the bottom-right corner of the grid (marked ‘Finish’ in the diagram below).
How many possible unique paths are there?
Above is a 3 x 7 grid. How many possible unique paths are there?
Note:m and n will be at most 100.
(机器人位于M x N网格的左上角(下图中标记为“开始”)。机器人只能在任何时间点向下或向右移动。机器人正试图到达网格的右下角(在下图中标记为“完成”)。有多少可能的唯一路径? 上面是一个3 x 7的网格。有多少可能的唯一路径? 注:M和N最多为100。 )
方法:动态规划
题目描述:
Follow up for “Unique Paths”:
Now consider if some obstacles are added to the grids. How many unique paths would there be?
An obstacle and empty space is marked as1and0respectively in the grid.
For example, There is one obstacle in the middle of a 3x3 grid as illustrated below.
[
[0,0,0],
[0,1,0],
[0,0,0]
]
The total number of unique paths is 2.
Note:m and n will be at most 100.
(独特路径”的后续行动:
现在考虑是否在网格中添加了一些障碍。有多少条独特的路?网格中的障碍物和空白区域分别标记为1和0。例如,如下图所示,3x3 网格中间有一个障碍物。
[
[0,0,0],
[0,1,0],
[0,0,0]
]
唯一路径的总数为2。 注:M和N最多为100。)
方法:动态规划
题目描述:
Given a m x n grid filled with non-negative numbers,find a path from top left to bottom right which minimizes the sum of all numbers along its path.
Note: You can only move either down or right at any point in time.
(给定一个由非负数填充的m x n网格,找到一条从左上到右下的路径,该路径使沿其路径的所有数字之和最小化。注意:只能在任何时间点向下或向右移动。)
方法:动态规划